Software is getting slower more rapidly than hardware becomes faster.
 
Note: This book was intended to be released in printed format. If you are using an electronic reader you might see small formatting issues. This is because Amazon's editor (Kindle Create) cannot import all features from a Microsoft Word file.
This book is dedicated to all programmers out there who still use a text editor as IDE and use printf for debugging.
 
 
 
 
What you will learn from thisbook
The purpose of this book is to kick-start you in programming true cross-platform GUI applications. Even if the stake is high I promise you it will NOT be a bumpy road. The learning curve is not steep and you only need ONE tool (IDE) for this.
Another purpose of this book is to help you decide which is the best tool for developing cross-platform GUI applications. I will constantly compare the VCL (the framework we will use) with other similar frameworks (C++, QT, .Net, etc.) and use references abundantly to back up my hypothesis/claims.
Once we finish reviewing the VCL and what it can do for you I will give you the one tool (the only tool) necessary to start programming your first multi-platform GUI application.
The book was written in such a way that you can read it without installing any programming tools. Once you reach the final part of the book, you can finally install the IDE and build your first “Hello world” application (see chapter “Your first Hello World application”). At that point, it will only take one minute to build your first application. I promise.
The book will also give you a crash course
into programming; however, it will assume that you have the most
basic concepts of programming (what is
a variable, what is a function).
Not a 500-page book
There are many thick books out there that tell you that if you want
to save the file, you go to the File menu and then click the Save
item that has an icon like a blue floppy disk, and then they show you
a screenshot of that menu.
I won't do that cheesy thing here. If
you read this book, you are a programmer. If you are a programmer,
you are smart. A smart person will figure out by itself how to
achieve such mundane tasks.
Summary
In this book we will talk about:
What is the VCL technology
Why VCL is better than QT (or other similar frameworks)
Which languages are using the VCL technology
Pros and cons of those languages
How VCL helps you develop safe applications
Which platforms can you target with VCL (I can tell you by now: Windows, Linux, macOS, iOS, Android and more)
On GUI apps
Why building GUI applications is still a challenge in 2020?
Everything starts small. Remember that guy in a garage called Bill Gates that started a small company called Microsoft. The same story applies to programming languages. A “guy in a garage” starts one day to program his own compiler. A GUI framework requires a lot of code. Therefore, as a single guy with no resources (aka money) the very last thing on his ToDo list is the GUI. So, he designs his entire language without thinking too much about GUI. He puts his compiler online and 10 years later a big-enough project starts to use his compiler. That compiler becomes more and more popular each day. But one day somebody asks: how do I create a GUI application with this compiler?
This is when all hell breaks loose.
If we take a look at C++ for example, a C++ programmer will have to rely on numerous “external” libraries and tools like QT, GTK, WxWidgets., XML, MFC, Boost, etc, in order to be able to build a GUI because C++ does not have built-in support for GUI applications. Even with all those 3rd party tools, creating a GUI in C++ is not a fun task.
Because of this, most C++ programmers would easily create console (aka CLI/command-line interface) applications but would cringe when they would be requested to create a GUI application. For Linux that would not be a big issue. Linux users can cope with console applications. But Linux is a very very small world. I know that some Linux guys even have a hard time to believe there is a whole world out there using GUIs.
Just try to imagine how the world would be if your grandfather would have to type a bunch of commands in a console in order to view the email he just received containing a picture of his newborn grandson.
Figure it out: A Linux guru or a grandpa trying to send an email?
How would you feel if you would want to listen to some music, but in order to start YouTube, you will have to poke some obscure commands in a non-GUI browser? Imagine a 3D shooter as a text-based game!
Hell, the year is 2020 and there are still people doing programming with nothing more than a text editor. This inefficient way of programming was suitable for the 60s, maybe 70s when computers had less than an MB of memory.
Be a better person. Do a proper job. Never write a console application from now on. With moderns IDEs today every application you build should be a piece of tiny jewelry.
Anyway, forget everything I just said until now and ask yourself: If I build a commercial application, how do I sell it better? As cool looking, feature-rich application or as a CLI program?
A computer program is just a tool
If you want to sell your applications, you need to meet the customer where it stands. Some programmers that can only do console applications will try to shove on customer's throat a console-based application and call this process “educating the customer”. Don’t do it like that. The customer doesn’t need to deal with your program’s quirks. The customer doesn’t have to learn your program's command-line syntax. The customer just wants to use the program.
Think about a hammer. You want to be able to use it without knowing the chemistry of the metals in it.
Use the correct approach from the beginning: instead of trying to convince the customer to change their needs to meet your programming tool/style/possibilities, convince yourself to use the proper programming tool and implement customer’s requests EXACTLY as they are!
Why GUI?
Probably I can load a bucket full with reasons why to prefer GUI over console applications but here are only a few of them (no particular order):
Console applications are flexible (not always), but GUIs are more “Natural”.
Console applications are very error-prone. The user could easily mistake the –i switch which in one CLI application means “input” but in another application means “delete all”. GUI applications give you real/visual feedback.
You can only present a limited amount of information in a console app and only in a rudimentary/basic way.
You cannot use a console app until you read its documentation (literary). On the other side, GUI applications don't really need manuals. Have you ever opened the user manual of Winamp or Firefox or MSPaint? I thought so.
MANY applications cannot be written as a console application.
Average PC users (the bulk of the users on the planet) are not computer-savvy enough to be able to use console applications.
Users want to analyze and visualize their data and not to learn yet another CLI command syntax.
Is programming a
GUI time-consuming?
If you are coming from the console world, you need to give up on the cliché things you used to know about programming GUI apps. The most famous one is that programming a GUI app is more time consuming than programming a console app. It is not. Well… at least not if you are using the correct programming tool :).
Takes me 60 seconds to build a GUI app that does a search string, equivalent of a RegExp (yes, I hate RegExp). If you think that my 10-line application will run flawlessly from the beginning and that the RegEx will probably not, and if you consider the time necessary to debug the RegExp script, I probably gained a lot of time today, choosing the GUI way over some obscure scripting. Also, note that my application will run on ANY computer while your RegExp script will depend on RegExp being installed there, user knowing the command-line syntax, etc.
Console vs GUI - There is no chasm!
Programmers that never programmed a GUI (or used the inappropriate tools to build one) think that it is difficult to separate the business logic from the GUI. They also think that a console tool cannot be implemented as a GUI application because you will lose its capability to be piped. It isn’t true. At all. You can easily design your app in such a way that it works as GUI and as CLI at the same time! I wrote hundreds of GUI applications. DNA Sequence Assembler is one of the complex ones. The program compiles a total of 4 million lines of code. This does not prevent me from making the program available also as CLI.
The program was initially developed as a GUI application, but it took less than 1000 lines of code to “convert” it to CLI.
A
CLI variant of the program presented above.
One can achieve this by ensuring that the business logic code does not access directly the GUI, not for input neither for output. For example, if you have a piece of code that needs to process a group of files, put the code in a class. Instantiate the class and pass a list of files to it – you will read the list from GUI if you are in GUI mode or from the command line if you are in CLI mode. As the code executes, don’t display the results directly into the GUI, use an intermediary Log to do this. If you are in GUI mode, the log will show the results into the GUI. If you are in console mode, it will display the results into the console.
Summary
The year is 2020.
PCs are commodities today.
CLI does not belong on (regular) user’s desktop.
It is time to get rid of obsolete languages that cannot build GUI applications natively.
If you use the appropriate tools, building GUI is faster than building CLI.
We have the tools to create cross-platform GUI applications with code that is 100% portable.
Introducing the VCL
The answer to the problems enumerated above is Delphi with its VCL/FMX framework. From its inception, Delphi was built with GUI in mind. Oh… let me correct that: the main purpose of Delphi was to build GUI applications.
For
those who never heard of Delphi, Delphi is Pascal on steroids and VCL
stands for Visual Component Library. VCL is written in Pascal and it
is used by Delphi, Lazarus and C++ Builder (C++ Builder is built on
top of Delphi’s VCL).
A super short history of Delphi (milestones only):
1968: Pascal
1983: Turbo Pascal (Borland epoch)
1994: First Delphi (first VCL)
1995: Delphi 32 bit
1998: C++ Builder
2001: Delphi for Linux (aka Kylix)
2003: Delphi for .Net (C#)
2008: Embarcadero epoch (Embarcadero purchases Delphi from Borland)
2011: Delphi FireMonkey (full cross-platform capabilities)
2019: Delphi now is at version 21 and keep counting
Until today Delphi had 30 major releases or 40 if you count Turbo Pascal. Each release has multiple subversions (updates). C++ Builder: 25 releases.
There are 15 current Pascal compilers and 7 Pascal dialects out there.
RAD Studio
Today Embarcadero offers VCL as part of its RAD Studio. RAD stands for Rapid Application Development. RAD Studio is an IDE that allows you to build Windows, Mac, Linux or mobile applications in no time. For example, it took me half a minute to start a new cross-platform project. In this half a minute I had time to drag and drop a few basic visual controls (main menu, close button, text editor), arrange them, add the code to close the application when the user clicks the Close button, compile the application and launch it.
RAD Studio has multiple personalities. A personality is a programing language that is loaded (automatically) into the IDE. The following personalities are supported by the RAD Studio:
Delphi personality
C++ personality
HTML 5 personality
PHP personality (today this is a standalone product)
Basically, you double click a Pas file and the IDE loads that file and you can start immediately programming in Delphi. You double click a C++ file and the IDE loads that file and you can start immediately programming in C++. You get the idea…
So basically, all personalities share the same IDE. You can switch personalities with a click. You can even mix Pascal and C++ in the same project and compile them together – Yes, I know, you heard it right; it is not a typo.
The RAD Studio with its PHP personality loaded
Note: I will mainly talk about Delphi in this book but most of the IDE features, technique, tools, plugins, debuggers, even VCL code presented in this book also apply to C++ Builder and/or Lazarus (Lazarus is a free Delphi-clone).
RAD Studio is one of the most mature and most polished IDEs. There are simply way too many features and we will need a full book to cover this. I will only enumerate the basic ones:
Customizable, pluginable IDE
WYSIWYG GUI editor
Tight integration between code editing and GUI design
Tight integration with the debugger
Error Insight (real-time error-checking)
Syntax highlight, compiler-assisted code typing (as you type the IDE will only suggest things that make sense in the context of your code)
Internal versioning and automatic backup system
External versioning (Git/Tortoise SVN)
Remote debugging
This PDF (24 pages) lists some of the features introduced recently in RAD Studio 10 Update 3.
No need to EVER leave the IDE. All necessary programming tasks are done from the IDE:
Edit your code/GUI
Compile and run your program
Debug
Do unit testing
Add/remove files from the project
Post-build events
Versioning
Prepare an installer (and upload the program on your website), etc
IDE Experts
I use a set IDE plugins (aka experts) that make the environment more productive. I totally recommend them to you also:
CnPack - www.cnPack.org
GExperts - www.gExperts.org
DDevExtensions - www.IdeFixPack.de
You can even create your own plugins using OTA. The Open Tools API (OTA) is a set of interfaces that allow developers to add new features to the IDE. These plugins are called wizards or experts. Wizards can use the OTA interfaces to modify the IDE, obtain information about the IDE’s state, and receive notification of important events.
More plugins and tools here.
What can you build right out of the box, with the VCL?
The VCL framework offers visual components in more than 50 categories:
Each category can contain a wide range of visual components:
You have so many visual components that you can build almost any application that crosses your mind right at this moment, right out of the box.
You can build multimedia viewers, image processing tools, graphic editors, advanced text editors, chat boxes, internet servers, tools for fast mathematical computations, DLLs, DLL plugins for other applications (like Winamp plugins), applications that connect to cloud computing servers, touch screen GUIs, telemetry, etc. Honestly, I haven’t used more than 15% of those visual components in 25 years of experience.
And if the VCL components delivered in the box is not enough for you, hundreds more are available in the open-source Jedi library. Jedi is for Delphi what Boost is for C++ (and Boost is included in RAD Studio, by the way).
To help you comprehend how complete the VCL and the RTL (run time library) is, count the SLOC of RAD Studio and you will find that it has 12.1 million lines of actual code (blank lines, comments, and GUI elements are not counted).
Note: For those used with QT, Delphi’s visual components are equivalent to QT’s widgets. However, QT has a much much much more limited number of controls.
Advantages of applications built with VCL
Portable/Monolithic
Delphi will compile your code one single exe file. The initial exe file (for Windows OS at least) if rather big - about 4MB in size. But hey: what is 4MB today when everyone has a 50mbps Internet connection! However, the file size does not grow much after that, even if you add 10000 more lines of code.
The EXE file includes all code that the program requires to work on any Windows platform (see ‘No prerequisites required’ below for details) from Win2K to Windows 10.
No prerequisites required
Unlike C++, Java or DotNet, Delphi applications don’t require additional runtime libraries in order to run. Just let your users/customers download your product as ONE exe file and it will run on any machine without installing any additional libraries. dotNet programmers: suck on that :)
Cross-platform
The Delphi code is 100% portable, like “one ring to rule them all”. The same code will compile everywhere (Windows, Linux, macOS, iOS, Android). There is no other programming language that comes close to this.
Some
of the platforms available in RAD Studio.
Clicking “Build
all” will compile your application for all these platforms at
once.
Where can you use VCL?
Ideal for system tools, quick and dirty tools
Ideal for “customer-oriented” GUI applications but also for backend
Ideal for intranet, server applications, DB applications
Ideal for CPU-intensive applications(deep learning, scientific, etc)
Where you can’t use it?
This really made me put the hand at my forehead, pause 10 seconds and say: hmmmm….
With Delphi you can build any application you would build with any other contemporary language like C++, DotNet, Visual Basic, etc.
I think the only place where Delphi will not shine (but all the above-enumerated languages will fail also) is statistics and “untyped” data processing, where R, Python, and Mathlab is king.
C++ Builder
If you are already familiar with C++ you can take advantage of VCL without ever switching to Delphi. However, if you are a beginner and you are just looking for the right tool for programming GUI applications, and you don’t have a clear preference for a language, I strongly advise you to stick with Delphi since it is a much cleaner and easy to learn language.
As Delphi, C++ Builder is a personality of RAD Studio. It is WYSIWYG, it targets Windows, macOS, iOS, and Android.
Visual components developed in Delphi can be used in C++Builder with no modification.
C++ Builder supports the C++17 Clang compiler and can also compile Delphi code.
Delphi and C++ are binary “compatible”
Binary code generated by Delphi can easily be linked to applications generated by C++Builder (and vice versa) to generate an executable written in both Object Pascal and C++. The Delphi compiler emits C++ headers, allowing C++ code to link to Delphi classes and methods as though they were written in C++.
Debugging
Since both Delphi and C++ use the same back end linker, in debugging, we can step from C++ code into Delphi code transparently.
Pros (compared to standard C++)
Fully integrated IDE
Fully integrated visual component library
Runs out of the box: no single library, compiler, 3rd party tool, debugger, etc required
Has some new concepts (borrowed from Pascal) like Try/Finally, Closure pointers
Can compile Pascal files
You can mix Pascal and C code in the same project
Cons
Adds an unnecessary level of complexity on top of Delphi/VCL.
Some of its compilers are only partially compliant with C++11
C++ Builder is a weird mix between C++ and Pascal
No support for Linux (yet)
Not as popular as “classic” flavors of C++
Compared to Pascal, C/C++ is a very unsafe programming language
Summary
RAD Studio is a multi-language IDE.
Delphi and C++ can work together.
VCL is the most complete and also the most mature framework on the market.
VCL applications are small, fast and portable (run everywhere, no run-time libraries required) single EXE file.
Mature, WYSIWYG, feature-rich IDE
You can customize/enhance the IDE with plugins
Delphi
Many people think that Pascal is some kind of ancient language. They are right! Pascal is ancient, as it predates not only C but all current Top10 languages.
However! It is not obsolete!
It is more up to date than any other “modern” programming language out there. It is definitively more up to date than C++.
In the last 25 years, it had 30 releases. This means about one major release per year, plus updates released every few months. I challenge you to name another language that comes close to this :)
Delphi language (also called ObjectPascal) is the continuation of Pascal language.
But Delphi is not just a language. Delphi is an environment that includes the language, the IDE, the VCL, and the compiler. Everything is integrated. You cannot have them separated. The IDE does not work without the compiler. You cannot compile without the IDE (probably).
(Note: From now on I will use Pascal/Delphi words interchangeably)
What are the disadvantages of Delphi
We have seen that Delphi has many advantages over similar low-level programming languages. Now it is time to see its disadvantages.
I worked with many programming languages but I mostly used Pascal. So, in order to be able to honestly answer this question I had to do a self-analysis to see if I am biased towards Pascal/Delphi.
The conclusion I reached is that I use Delphi not because I liked it more (unjustified bias) but because it DOES solve the problems that I have, faster and safer than other programming languages. It is not the way I am “biased” that led me to Delphi, it is the way in which Delphi is implemented (fast, clean, safe) that led to its usage.
So, now (I hope) I can honestly answer the question “what are the disadvantages of Delphi”.
The only BIG problem I see is that Delphi is not as popular as other programming languages (C++, Java). At the dawn of the computer era, Pascal was No1 (literary) programming language on the planet for may many years. However, Delphi's popularity fell down (see Tiobe index) as Borland was losing the fight against Microsoft. Because Delphi was better than Visual Basic, Microsoft actively worked to destroy Borland by “buying” important people that worked for Borland (for example the brains that made the nice. Cool, fast compiler). This article has delicious insights into that: “How Microsoft Schemed to Destroy Borland” (plaintiff exhibit). Today the original people that developed Delphi are scattered all over. For example, the guy that created the Delphi compiler is now the creator of DotNet.
Microsoft also planted Delphi-countermeasures in its operating system to detect and randomly crash Delphi applications.
Today Delphi is still a popular language but not a popular as it used to be:
Java |
1562400 |
C# |
1324180 |
C++ |
640600 |
Delphi |
44700 |
Gcc |
31880 |
Visual Basic |
32800 |
Visual Studio |
29220 |
C++ Builder |
2665 |
StackOverflow tags as of 2019
Libraries
One of the immediate consequences of its low popularity is that you will find fewer libraries for Pascal than for Python, Java, etc. However, I never encountered a situation where I needed a library for Delphi and could not find one. One of the most popular libraries is the open-source Jedi project, which is simply huge!
Freeware
A problem that Delphi used to have, was the lack of a free version. People want free stuff. No matter how crappy a programming tool/IDE is, people will choose it just because they are too cheap to invest some money in a decent tool.
Fortunately, Embarcadero offers today the Delphi Community Edition, which is free for non-commercial use.
Anyway, the Delphi Professional edition is not expensive. It sells for 1700 euros (about $1800). You only get one year free updates, unless you purchase the updated plan (not so expensive). At least they sell you a permanent license, not “pay us every year” as other companies (Microsoft…cough) do.
Brighter future
Whit the arrival of the free license I see a much brighter future for Delphi as schools will start again to use Delphi for teaching purposes.
You can already see Delphi getting traction again, as Delphi returned in Tiobe Top 20 index.
Summary
Pascal was in the top 5 programming languages between 1975 and 1996.
Delphi is not as popular as Java but it is still one of the top 20 programming languages today.
Delphi is gaining traction again.
The Pascal language
The Pascal language is super easy to learn. Probably it is the easiest language to learn, reason for which it was the main language teach in schools/universities in the past. Later in the book, we will have a crash course in Delphi.
The language is just a bit more verbose than C++ but it is definitively not as cryptic.
Example:
Delphi:
if IsNextSong then PlayNext else StopPlay;
C++:
(m_IsNextSong())? PlayNext():StopPlay();
A strongly-typed language
Delphi is a strongly typed language, which allows the language to be more secure (see “Language security” chapter).
Well designed
The language itself was so well designed from the very beginning that many patterns (see “gang of four”) are not even required in Delphi.
Another huge advantage of its design is that the Delphi code is always backward-compatible! Programs from the 80s-90s are still compiling today. There is no other programming language that has this characteristic.
The VCL framework is very mature (built 25 years ago) and stable.
One string to rule them all
Delphi has just one string (called “string”) that is super-fast because it does not rely on the #0 terminator at the end of the string as C++ does. Actually, Delphi strings are much like the strings in high-level languages (s:= s+ ‘more text here’) but much much MUCH faster. Delphi uses a very clever method to achieve this. We will study Delphi strings in the “Safer, faster strings” chapter.
Not open-source
One thing that might put off some programmers (especially Linux guys that are used to get everything for free) is that Delphi is not (quite) an open-source programming environment. It is “owned”.
However, I think this is actually a huge advantage, not a disadvantage as this is the reason for which Delphi is so up to date. The language does need to go through standardization.
For example, in the case of C++ language, a committee of a few hundred people has to gather several times per year. Each meeting takes weeks. No wonder why the process is so slow. With such a large crowd, of course, there will be different opinions, different interests, disagreements…
In the case of Delphi, Embarcadero (the owner) says “we need the x language feature, let’s implement it”, and… they just implement it. I see Embarcadero as a benevolent dictator, not a tyrant.
Other Pascal communities like FPC (FreePascalCompiler) are closely following changes introduced by Delphi each year so Delphi is also (without intention) the standardization factor for the Pascal world.
I said above that Delphi is quite open-source. What I mean is that when you purchase a license you do get the source code for the entire VCL/FMX/RTL framework. So you can customize Delphi to tailor your needs and you can learn from its source code.
Compared to C++
Pascal doesn’t have any of the main problems of C++ such as over-complexity, lack of reflection, perceived feature creep, lack of unity.
Pascal lacks
Operator
overloading (except for the array subscript operator [..])
Never
to be implemented in Pascal because of safety
issues.
Multiple inheritances
Many people think that Delphi cannot achieve multiple inheritance. However, Delphi achieves it via multiple-interfaces (similar to Java and C#).
Templates
The first C ever to contain templates was released by Borland in 1993. But since 2009 Delphi also has templates (actually called “generics”).
Fastest compiler
Most importantly it does not have the WORST problem of C++: slow compilation time. Delphi can compile 1 million lines of code in seconds. We will take an in-depth look at this in the ‘Compiler Speed’ chapter.
Better error messages
In C++ most compiler messages are the ambiguous (particularly from template metaprogramming). However, Pascal is a clean language, so the compiler always knows with which kind of variable/object it deals during compilation. Therefore, when something goes wrong, the compiler can figure out exactly what was wrong and issue a useful warning.
Also, the compiler is super accurate about the error location. In case of an error, it highlights the line where you have that error.
No more pointers
As C++, Pascal is also a low-level programming language therefore it also needs pointers. But the pointers are hidden from the programmer, making the language much safer.
As a Pascal programmer, it is unlikely that you will ever need pointers. However, if you love to live on the edge, the pointers are there. You can use them.
ASM in the mix
Another cool feature that is not available in most languages is that you can mix Assembly and Pascal code in the same file, even in the same function.
Execution speed
“A performance comparison of C#, Delphi and Python languages” white-paper finds Delphi 3 times faster than C# and 49 times faster than Python and also uses 50% less memory.
Another test finds Delphi as fast as VS C++ and MinGW. We can also see similar speeds in applications compiled with Lazarus (see ‘Lazarus’ chapter).
The never-ending story
And because it looks good on paper, I will enumerate below blindly some of the unique features of Pascal language that cross my mind now: procedures, single file units (no more headers), sets, sub-ranges, class types, class reference variables, virtual class methods, virtual constructors, extensive runtime type-information (RTTI), message methods, dynamic methods, the override keyword, the inherit keyword, two methods/functions with the same name (overloading) without dirty cheats like name mangling, properties, the try..finally clause, initialization and finalization sections, variants, threadvars, native DLL support, native COM support, interfaces, packages, interface delegation, automatic reference counting mechanism, method pointers.
The compiler
The
Delphi compiler is really stable. Unlike C++, compiler bugs are
seldom and usually, they are self-reporting: instead of silently
corrupting your binary code, the compiler will show an ICE (Internal
Compiler Error) message.
I encountered only twice a compiler bug
in 25 years of Delphi programming.
Because of the way the language was designed (no header files, no precompiler), Delphi requires no toolchain! When building your application, all you have to do is to press one single button (Compile or Run) to have your application compiled. That’s it.
Everything the IDE has to do to compile your application, it does it behind the curtains. You don’t need to set up a toolchain; you don’t need to care where the VCL libraries are located; you don’t need to worry about the precompiler.
Compiler speed
The execution time of Delphi programs is similar to the execution time of C++ programs (Delphi programs are only marginally faster).
However, the compilation speed is a different matter. There is a quote on the Internet that says “You want to upset a C++ programmer? Ask him about his compiler speed”.
Pascal compilers are way much faster than the fastest C++ compiler. The technical reasons are:
Cleaner language
Strongly typed language
No “include” hell. Delphi has no header/include files
No pre-compiler needed
A trick that Delphi uses to make the compilation fast is called the interface signature. Delphi computes a checksum for all function declarations in your code. If the code was once compiled and never changed since the last compilation, then the compiler knows that it doesn’t have to recompile it next time you hit the Compile button. Also, the entire RTL/VCL library comes precompiled, so basically, you never have to compile it.
The ‘Compiling multi-million line C++ codebases “effortlessly”’ video (1/2 hours) shows the struggle of a company to compile multi-million lines of code with C++.
On the other hand, the “Compiling a Million Lines of Code with Delphi” video (two minutes) from Embarcadero shows how Delphi compiles 1 million lines of code in under 5 seconds.
To compile my own code 0.2 million lines of actual code (counting no 3rd party libs, no comments, no blank spaces) the compiler needs way under 1 sec.
The Memory manager
The memory management in Delphi is unique because ALL memory is allocated through an internal memory manager called FastMM. This memory manager is compiled into each of your applications.
There are multiple advantages to this:
Dramatic speed increase for memory-bound operations
Memory allocations are very expensive: a typical C++ program would have to go to the OS to request memory, the OS has to check its internal tables and if no suitable block is already available it has to go to the MCU obtain the block of memory, and then update its internal tables.
FastMM employs an algorithm that anticipates the memory needs of your program and pre-allocates memory.
Pre-allocating memory will dramatically increase the speed of the program.
Prevents memory fragmentation
Memory fragmentation appears when applications repeatedly ask for small amounts (let’s say 1 KB) of memory then release that memory and then request memory again, etc. The memory of the system could be almost free since the blocks allocated are small. However, if you ask for a contiguous block of memory of let’s say 200MB (a very small value today), the OS might not be able to find such block even though 2GB out of a total of 4 are free. Why? Because the 2GB of free memory is now fragmented by tiny blocks of occupied memory, scattered all over your memory.
Here is an experiment you can do to actually fragment your memory:
Allocate about half the available memory in 12 byte blocks.
Free every second pointer that you allocated above. This will freeup half of the used memory.
Now allocate half of memory previously released, in 20 byte blocks.
Step 3 will fail even if the system has the amount of memory we want to allocate, free. The 20 byte blocks we want to allocate, cannot fit into the 12 byte blocks of free memory.
Pre-allocation memory also helps reduce address space (memory) fragmentation. So, Delphi applications are not only faster at allocating memory, but they also play nice with other applications running in the system.
Better memory sharing
If you want to share data between your applications and DLLs, FastMM provides a dedicated sharing mechanism.
You can easily share basic data types (strings for example) between DLL and EXE files by using ShareMM.
Complex data structures (objects, forms, etc) can also be shared by using Delphi BPL packages. BPLs are DLLs on steroids.
Notes and links
Delphi supports Garbage Collector when it compiles for ARM platforms.
There is a multi-platform optimized version of FastMM here.
If you really want details about how to share advanced structures see Putting classes in a DLL and Delphi forms in DLLs.
Summary
Delphi has the fastest compiler (0.2 mil lines of code per second).
Delphi has an integrated memory manager which makes all memory allocations fast and prevents memory fragmentation.
Delphi is not open source (but you have access to its source code).
Delphi is freeware (via Community Edition).
Safety
I kept talking in this book about how Delphi is much safer than C++. Below are yet two more unique features that make Delphi safer. The first feature comes from the memory manager. The second comes from the compiler.
Always use these features into your program, and never go to sleep without thanking the programming gods for it, because it will help you achieve something that many programmers think is unachievable: bug-free programs! BAM! I knocked you down with this phrase, right?
FastMM - No more memory leaks
FastMM provides memory related self-reporting functions to help application monitor their own memory usage and report potential memory leaks. Because of this, memory leaks are impossible in Delphi applications. L I T E R A R Y!
When in debug mode, FastMM will automatically report any memory leak. To switch FastMM to debug mode, just add the FastMM unit on the first line in your project file:
When a leak was detected, FastMM will generate a leak report on application shutdown. The report contains a stack trace to the line of code that allocated the memory and “forgot” to release it. The object that leaked the memory is also listed in the report. All you have to do is to open the report (a txt file generated in the application’s folder) and use it to locate the faulty line.
There are many cool debugging features that you can customize in FastMM. The customization is done via an INI file. But INI files are boring so better download the FastMM Options Interface tool.
You can find more details about how to customize your FastMM here.
Enabling FastMM additional features for advanced memory leak reporting
Warning:
When FastMM is running in debug mode, you will need to have the FastMM_FullDebugMode.dll present in the same folder as your application. So, when you ship your application, remember to switch FastMM back to non-debug mode OR to ship the mentioned DLL with your application.
(FastMM) No more stack corruption
Stack corruption is, without doubt, the ultimate nightmare of a programmer. Once the stack was corrupted, you cannot trace anymore the execution path to the line of code that caused the corruption because well... the stack was corrupted. The source of the error is simply untraceable!
And you should call yourself lucky to get to know that the stack was corrupted because in some cases the stack is corrupted in such a way that it gives the impression that it was not corrupted, sending the programmer in a goose chase.
Unfortunately, the stack can be easily corrupted. Programming languages like C++ are prone to stack corruption because they use the stack a lot. C++ prefers to allocate things in the stack because C++ is slow in allocating heap memory (we just have seen above why).
For example, in C++ all you have to do is to allocate a structure of 100 bytes and write in it (by accident) 100000 bytes.
FastMM makes Delphi not only fast but also safer because the stack is not really used.
In Delphi, all local variables of fixed size (mark this down, it is important), for example, integers, booleans, floats, are stored in the stack. These operations are safe because the variable has a FIXED size so there can be no accidental stack corruption.
However, all possibly dangerous types (which means dynamic-size structures: objects, strings, arrays) are allocated on the heap.
(FastMM) No more access violations
FastMM offers two additional unique debugging features:
Preventing access to freed objects
When this feature is enabled, FastMM keeps track of all freed objects so it knows when you are trying to access an object that was already freed. In this case, your program will get suspended and FastMM will report the deleted object that the program tried to access, who tries to access it (which line of code) and also a stack trace.
Preventing access on dangling pointers
When
this feature is enabled, FastMM can write special patterns in memory
so it knows when you are trying to access a dangling
pointer.
This operation makes the program terribly slow, but who cares.
Better wait 2 minutes for the program to start up than to waste days
weeks doing painful code inspection/debugging.
A better compiler
Compiler warnings
Delphi compiler is less lenient towards sketchy code. You either write good code or you won't compile!
Whenever you write flimsy code, the compiler will issue a hint or a warning.
But don’t worry! Unlike C++ compilers, the Delphi compiler is not snarky. It will politely tell you why the code is wrong. The compiler messages are not obfuscated and/or cryptic so you can easily fix your code.
The compiler begs you to fix your code by using the correct assignment symbol
( which is := )
Runtime error checking
Runtime error checking is a generic name for three types of binary code that the compiler can inject into your program behind the curtains, to find possible bugs:
1. Overflow checking
This will check certain integer arithmetic operations (+, -, *, Abs, Sqr, Succ, Pred, Inc, and Dec) for overflow. For example, after a + (addition) operation the compiler will insert additional binary code that verifies that the result of the operation is within the supported range.
2. IO checking
Enables the automatic code generation that checks the result of a call to an I/O procedure. If an I/O procedure returns false, an exception is raised. If this switch is off, you must check for I/O errors manually.
3. Range Checking
The Delphi Geek calls this “The most important Delphi setting” and I totally agree. It checks if all array and string indexing expressions are within the defined bounds. It also checks that all assignments to scalar and subrange variables are within range.
Here is an example of code that would ruin your life if Range Checking would not be available:
Type
MyArray= array [1..10] of byte; // we define an array
x:= MyArray[20]; // Range Checking will prevent the program from accessing element 20 (ERangecheckError exception is raised). Security breach avoided. Error log automatically sent to the programmer. Bruce Willis saves everyone.
If any of the above runtime checks fail, an exception (that the bugger will catch) is raised.
Enabling Runtime Error Checking
To activate the Runtime Error Checking go to Project Options and check these 3 boxes:
E
nabling
the Runtime Error Checking in ‘Project Options’
Usually, you activate Runtime Error Checking and forget about it. Until one day when you write one line of code and run the program only to see that now the program raises an exception. You look back to the line that you just added and realize that yes, the line is buggy. Without Runtime error checking that bug would have crept in unnoticed (no exception raised) and one week later, on the next product release, the complaints would have poured in. How much time would you have spent finding that bug? Runtime error checking just saved your ass.
Blatant
advertisement insert:
Do you like this book? Post a short review about it on Tweet/Facebook, star it on Amazon, send the link to your girl-friend, go outside and shout its name so all neighbors can hear you. Thanks.
A word of warning: everything nice comes with a price (fortunately a small price): enabling Runtime error checking slows down your program and makes it somewhat larger. Computers today have lots of RAM so the increase in size is irrelevant. Let’s put that aside.
However, the execution speed is slowed down significantly. I would say with a whopping 25% maybe more. So, if you have a program where speed is critical, you better activate Runtime error checking during development/debugging only. What I do, I also leave it active in the first release and wait a few weeks. If no bugs are reported by Runtime error checking, then I release an update in which Runtime error checking is off.
PS: Note that “IO checking” can always be left active. The performance hit because of this runtime check is minimal to zero.
Faster strings
The most common errors in C++ programs are buffer overflows generated when you try to access a data structure (most commonly strings) beyond its endpoint.
There is no buffer overflow or range checking in C++. The way C++ strings are built makes this kind of checking difficult. In Delphi, buffer overflows are almost impossible.
Safer strings (aka the infamous #0)
As mentioned before, Delphi strings are similar to strings of high-level programming languages. However, they are not objects. They are allocated instantly without all the overhead that appears around object creation/manipulation. Also, opposite to C++, Delphi strings are fast because they don’t rely on the infamous #0 string terminator. In other words, you don’t need to check ALL characters in a string to see where the string ends. That will hurt your program a lot when you work with many and/or long strings!
A reliable compiler
When I moved to the C++ world, I often heard people saying “Oh... that's a compiler bug”.
I have seen with my own sensors (eyeballs) programmers trying to find bugs in their code - bugs that never existed. The C++ code was ok. The bug was actually in the binary generated by the compiler. A programmer using this kind of tool is no better than a programmer tossing a coin, as there is no way to detect, prevent and fix this kind of bugs. You are at the mercy of the stochastics Gods.
While doing research for this book, I tried to list ALL compilers bugs in the GCC compiler on the “GCC Bugzilla” webpage but I got this error message from the server: “This list is too long for Bugzilla's little mind”. After waiting a good minute, finally, a web page was generated but only the first 3000 entries were listed. Man! That’s a big number.
I don’t say there are NO bugs in the Delphi compiler. Maybe there are, but probably some order of magnitude fewer.
For example, the last Delphi update contains only 46 compiler updates and bug fixes (sorry I was too lazy to actually disgorge the compiler bugs from the compiler updates).
Also, note that most of the bugs are self-reporting internal compiler errors (ICE). Instead of just generating the wrong code, the compiler will simply issue a warning.
Other bugs are minor or “easy-to-spot” bugs like “Compiler does not generate exe file sometimes“.
On FreeAndNil
Usually, Delphi programmers use Free to delete an object when not
needed anymore (for example SomeObject.Free). However, instead of
using Free I use FreeAndNil(SomeObject). This tiny change will save
days if not weeks next time when I make the mistake of accessing an
object after I free it.
Free destroys the object but the
SomeObject variable still points to the memory location where the
object was. The memory manager will not fill the memory occupied by
the object with zeros, because this operation is expensive. It will
merely mark the block of memory belonging to that object as “not
in use”. Every single bit of binary code belonging to that
object is still there, in memory. Untouched. If you try to access the
object now, you will probably be able to do it. So your program will
work normally, as nothing happen.
But sooner or later, as you change your code, the memory manager will claim back that piece of memory (as that piece is marked as free/available). This is when the hell will break loose on you. You are trying to access now via SomeObject a piece of memory that is not yours anymore:
You are Screwed! (capital S)
What FreeAndNil does, it first frees the object then it sets the SomeObject variable to null. If you try to access now the freed object via myObj, your program will instantly let you know by raising an access violation (AV). Remember, you are using Delphi now, which is debugger-friendly: access violations are your best friends!
When an access violation is raised, the debugger will step in and put the cursor on the line of code that generated the violation. You will be able to locate and fix the bug in seconds instead of weeks.
Remember this piece of advice if you do not want to burn the midnight oil debugging code!
If you use 3rd party code that relies on Free instead of FreeAndNil, and you have access violations, try to set the memory manager (see the FastMM chapter above) to fill the memory of the freed objects with zeros. This will be as successful as FreeAndNil in finding bugs but it will slow down the program a lot. I am not kidding! Use this as last resort.
A word of warning on Application.ProcessMessages
By default Delphi, as many other programming languages is not multithreaded. The code you write executes in the same thread as the GUI/VCL code. This means that if your code does some lengthy operations, the GUI freezes until that operation is over.
You may see people using ‘Application.ProcessMessages’ in order to prevent the GUI from freezing during lengthy operations. Don’t do it! If you don’t understand the depths of the Windows message queue, it can create lots of problems. If you do understand the depths of the Windows message queue, it can still create lots of problems. Anyway, if you decide to rely on Application.ProcessMessages you will have to lock (disable) the GUI when you start a lengthy operation to make sure that the user will not press the 'Start' button until the lengthy operation is over. Once the operation is done you need to unlock the GUI. Note that before locking some GUI elements might have already been locked (disabled). Those elements need to remain as they are. In other words, you cannot simply enable and disable ALL controls blindly.
One recipe for guaranteed disaster is to call Application.ProcessMessages from inside of a timer. Never do that!
If you have to do some heavy calculations that will freeze the GUI for a few seconds, it is ok to let the GUI freeze. Just put the cursor to crBusy and the user knows that your application is busy. You might think that the user won't be able to press the 'Cancel' button to cancel the lengthy operation. That is indeed a problem. However, there is a workaround: you can instruct the user to press the Esc key. You can read the keyboard from time to time to see if the user pressed the Esc key and abort.
If you know that the operation might take more than 20-30 seconds, show a progress bar. For more than 60 seconds do the proper thing: put the lengthy code in a thread. Let the thread inform the GUI when it is done calculating.
Always remember that the fact that the GUI freezes during lengthy operation is not a bug! It is a feature of the VCL that protects you from the dreadful reentry issue. Delphi did it for a purpose. Don't work hard to undermine it.
Summary
Delphi can generate code that actively checks for error at runtime.
Delphi has a reliable compiler.
Delphi compiler messages are accurate and easy to understand.
Never build your program without Runtime Error Checking.
Treat the compiler hints as they are warnings and the warnings as they are errors!
Keep in mind that the GUI will freeze during lengthy operations. Work with it, not against it!
Automatic bug reporting
The next tool I will present is the only tool that you really need but it is not delivered with Delphi. Even more, you have to pay for it.
This tool is called a bug reporting tool. There are three available: the EurekaLog, the MadShi madExcept, and the Jedi JclDebug. EurekaLog is the most advanced but it is quite buggy. The price is $149. MadShi is 159€ or free for non-commercial applications. Jedi tool is free but not as polished as the first two.
A bug reporting tool will insert special binary code into your application. When an exception is raised, the bug reporting tool will catch it and send a bug report to you. The bug report can be sent via email, FTP, Jira, mantis, etc and it is very comprehensive. It can include details about the hardware/software running in the target computer, the exact line of code where the error occurred and the stack trace. A bug reporting tool can help you to achieve the unachievable: bug-free programs. Also because you are notified immediately, you can offer ZeroDay bug fixes to your customers.
A zero-day bug fix is when you discover a bug on the product release day and you offer a patch on the same day also.
Write an application and relax… while waiting for bug reports to pour in (grin emoji).
An EurekaLog bug report in „detailed“ mode.
Configuration panels for EurekaLog.
A preview of the bug report sent to the developer automatically.
Configuration
panel for MadExcept.
The magic of automatic bug reporting without
writing a single line of code.
Summary
Dramatically decrease time spent on hunting bugs.
Detect bugs and release fixes on the same day.
The “burning monkey”
Up to this point, in order to keep things simple, I only talked about the VCL framework and I omitted an important piece of information: the VCL framework only works on Microsoft Windows. There is actually another framework called FMX. This is the actual framework that you will use in your cross-platform applications. Don’t worry, you don’t have to unlearn any of the things that I have told you until now. Whatever I said about VCL is also true about FMX.
By the way, FMX stands for ‘Firemonkey framework’.
A bit of history
As I said previously, back in ‘94 Delphi was designed with GUI in mind. Delphi language and the VCL were born together. VCL was designed for Windows only because back then Mac and Linux where rather curiosities. They didn’t really have a niche in the market.
However, arround the year 2000, Linux started to get some traction. Delphi had to quickly adapt to support this OS. By that time, the VCL framework was already mature. It would have been difficult to modify it now for cross-platform. Instead of patching the VCL to work on Linux they took the decision of rebuilding the visual component library from scratch. If you ask me, I think that was a really good decision: why do some half-ass job when you can do it anew and properly.
The CLX cross-platform framework was born. And cool enough the Lazarus also embraced this framework.
However, a (commercially) wrong decision was made: Borland went for Linux instead of going for Mac first. As we know Linux users want free software. Delphi was not freeware! Therefore, Delphi never gained traction on Linux. They should have gone for Mac first and Linux later (never) as Mac users will more quickly open their wallets.
Honestly, I don't think that Linux users will ever embrace RAD studio. There will be SOME Windows programmers like me that will write apps for Linux, but true Linux users programming in a graphic IDE... I don't see it happening.
Today FMX is CLX reincarnated, and it supports all major platforms: Win, macOS, iOS, Android. Linux support was recently added also.
Firemoneky = VCL on steroids
As stated, everything we learned about VCL applies also to FMX. But FMX has few extra features on its sleeve, features that VCL lacks:
FMX is vectorial (VCL is raster) and uses float precision for positioning controls (instead of integers) which results in more fluid interfaces
Fully high-DPI aware
Ready for 2D and 3D interfaces (provides a 3D scene environment useful for developing visualizations)
FMX can do complex GUI animations with slick transitions between screens and over 40 effects like reflections, shadows, blur, glow, alpha & translucency, and other eye-candy effects.
But the coolest thing is that FMX has built-in support for GPU acceleration (shaders/tessellation, etc). All those sleek animations in your app will be powered directly by your 3D graphics card, and you have to write not even a single line of code for that.
Video tutorial: Creating_a_3D_application
Disadvantages of FMX
Existing VCL applications need to be converted to FireMonkey. But since you are reading this book, this isn't probably your case. However, in case you already have an existing VCL application and want to port it to FMX, Mida and MonkeyGrommer will help you with this task.
From a technical point of view, the FMX approach is not perfect. Lazarus was wiser by embracing CLX instead of VCL or FMX.
Summary
FMX allows cross-platform apps
Sleek effects and animations
Powered directly by your 3D card
Let there be styles
I mentioned before that Delphi applications support styles. Before talking more about styles, let me ask:
How many lines of code do you think that is needed to write a GUI like this?
(Note: If you
read the printed book these images will be black&white.
Imagine
they are colorful and eye-candy!)
You need NO code to make your GUI look like one of the above screenshots. Delphi has built-in support for styles. Just open your project’s options (Shift+Ctrl+F11) and choose which style you want to apply to your GUI.
Run your application and it will look exactly like one of the styles above.
How great is that?
How to apply styles to your application?
To
apply one style to your application just open Project Options
(Ctrl+Shift+F11), go to Application → Appearance and you will
see a list of styles:
S
ome
of the many styles shipped with Delphi.
Check the one that you want to use. Notice that there is a Preview button that will let you see how your application will look like if you apply that style. Press OK when done. Delphi will automatically package the style(s) you selected into your EXE file. Run your application and admire its beauty.
If you use the default styles delivered with Delphi, as you can see, there isn’t much you need to know about it. Styles will work right out of the box. However, if you want to know more see this video from Embarcadero.
But I want more than one style!
You can pack more that one style into your application. But you will have to use the “Default style” box to select which style is the active one.
But what if you want to let your customers choose their preferred style? That's is doable but you will have to write a lot of code for that. I'm joking. You only need to write about 12 lines of code for that. I put the code into the Appendix.
From where to get more styles?
Delphi ships with a decent collection of styles. You will find them as .vsf files (if you use VCL) or .style files (if you use FMX). If you don’t like those styles, you can purchase more from www.delphistyles.com
Once
you download the style file, put it into the following folder and
restart the IDE:
“c:\Users\Public\Documents\Embarcadero\Studio\xx.x\Styles”.
After
the restart, the new style will appear in the Project Options.
You can even make your own styles with the integrated style editor. Just look for tools like StyleDesigner.exe and StyleViewer.exe in your Delphi bin folder.
Summary
Use styles to create sleek looking, modern GUIs.
Only one click required to have your application styled.
You can create your own styles with the Style Editor provided.
Lazarus
Lazarus IDE running on Mac
Lazarus is an open-source free Delphi-clone. It has all the advantages enumerated above freeware, but it is a bit less polished than Delphi. Don’t let this put you down. It also has extra strengths; for example, it has better cross-platform support than Delphi.
Note: Lazarus it is actually only an IDE, specially designed for FPC (Free Pascal Compiler). But I will use here Lazarus and FPC interchangeably.
Lazarus does its best to be compatible with Delphi. If you already started a project in Delphi you can use the included converter wizard to migrate your Delphi project to Lazarus. If you have a big project, you might have to do minor adjustments to your code here and there but the migration effort is minimal and their website hosts a tutorial on how to do the migration.
Please note that once you converted a Delphi project to Lazarus you won’t be able to go back. There is no tool to do the conversion in the opposite way, so you will have to do the conversion manually (with little effort).
Lazarus does not have a dual framework (VCL/Firemonkey) as Delphi has. It only has CLX. If you remember CLX was initially developed by Borland and then abandoned, only to be replaced some years later by FMX.
Lazarus having a single framework (CLX) is a huge advantage: if you start a Windows project and later you want to make that project cross-platform, you don’t have to migrate it to FMX. Under Lazarus, all projects are cross-platform projects from the beginning.
Lazarus also can compile to over 50 platforms and 15 architectures: i386, AMD64 (x86-64), i8086, ARM AArch64, PowerPC, PowerPC64, m68k, SPARC, SPARC64, MIPS, AVR, JVM bytecode, LLVM IR, etc. If you are a QT fan, Lazarus also supports QT.
Lazarus motto: “Write Once, Compile Anywhere”
And while the Delphi IDE only runs on Windows, Lazarus IDE runs on multiple platforms: Nix, Mac, Windows, ReactOS.
And all these goodies come in a much smaller package: only 150MB (or something like that).
Of course, there has to be a disadvantage: there are fewer libraries for Lazarus than for Delphi. But if you use Lazarus and you really need a library that is available in Delphi, you can port it to Lazarus.
You will find some Lazarus code in the ‘Inheritance, polymorphism and typecasting’ chapter. The code compiles on Lazarus and Delphi if you modify two lines of code.
Summary
Lazarus is freeware
Supports more platforms than RAD Studio
Compatible with Delphi
Not as polished as RAD Studio
Overview
We learned a lot about Delphi: how it helps you build fast applications, how to build stylish programs, how to build safe programs. We have compared Delphi a lot with C++ and other languages.
Soon we will have our crash course in Delphi and we will build our first GUI application. But before going there, let’s summarize and see what we have learned.
Why building GUI apps is so easy with Delphi?
WYSIWYG
If you used to type code in order to build a GUI, those days are over now.
In Delphi, the entire GUI is built visually, with drag and drop, in a true WYSIWYG manner. No single line of code is necessary.
The Form Designer
You will use the Form Designer to create your application's GUI. The Form Designer keeps the GUI elements separated from your business code. The GUI elements go to a DFM (DelphiForm) file, while your business code goes to a Pascal (PAS) file.
Quick mockups
Delphi allows you to easily create GUI mockups. If you develop a new application for a customer, just invite the customer over (or use Skype) and build the GUI of the application right on spot. In less than 20 minutes you will have the entire GUI ready, exactly as the customer wants it.
The good news is that the mockups are not just mockups. You will actually use the “mockup” that you built with your customer, in the final product. The time used to build the “mockup” is not lost!
The VCL framework
The VCL framework gives you hundreds of visual components so you can build any type of program you can imagine. If one of the controls you need is not there, pretty sure you can find a free or commercial one on the Internet.
You want a triangular button or a green toolbar, or any other crazy component nobody dreamed to build until now? You can build it in no time.
Why building cross-platform apps is so easy with Delphi?
Portable code
Unlike many other programming languages, Delphi was built with multi-platform in mind. The code you write in Delphi is 100% portable, which means that if you want to perform a task (deleting a file for example) no matter if you are on Linux, Mac or Windows, your code will be the same.
Xamarin, for example, claims only 70% code portability but I think that is a bit exaggerated. And Xamarin GUI building is far from WYSIWYG.
Native look
As mentioned, your cross-platform application can be styled. This allows your application to have the native look of the platform on which it runs.
But with styles, you can achieve even more (strange) things. For example, you can make your application look in a particular way, no matter which platform it runs on. Want to deliver to your Windows customer an app that has the look of a Mac app? You can do it by applying the MacOS style.
Ready for the future
Delphi is up to date as it supports all modern technologies such as tablets, smartphones (full support for touch screen), cloud services (Amazon Cloud Service, Microsoft Azure), etc.
Note: Up to this date C++ Builder does not have yet support for Linux. You will have to use Delphi/Lazarus if you want to build Linux applications.
How/where I use Delphi/VCL
I used Delphi for almost 25 years and I built all kinds of applications with it. Some are just simple (200 lines of code tools) applications others are multi-million lines of code full-blown applications:
Multimedia
Video
chat, image processing, video applications, fully automated “kiosk”
for professional recording studios.
System
Low-level
system tools & Windows utilities, file handling/management.
Big data
RAM/CPU intensive apps, GUI on top of console apps (lots of them!), Hadoop integration (Linux), large binary file parsing.
Editors
HTML
editor, text editors.
Internet
Ftp clients, internet browsing, webpage scrapers, client-servers.
Desktop
“Desktop
branding”, remote debugging, desktop announcements, video on
desktop, webcam on desktop, etc.
Scientific
Bioinformatics, medical applications
Installing Delphi
If you are ready to install RAD Studio head to Embarcadero website and download the installer which is about 2GB. The total required space on the drive after installation is about 8 GB. You need about 5 minutes to initiate the installation process. Once started, let it download and install the modules you selected.
First, you will need to install DotNet. It is already provided in the package.
Then the installer will also let you choose which modules (samples, help) and also which platforms (Windows, OS X, Android, etc) to install.
If you choose to develop for cross-platform you will also need to install Android SDK and Java.
Other modules that you can install optionally:
Interbase Express – a DB engine
TeeChart – a visual component for drawing complex charts
IntraWeb – a framework for building web-oriented applications (like web servers). Is support REST, FaceBook, WordPress, BootStrap, etc.
W
hen
done, you can immediately start running the demos provided, writing a
Hello World or building your own apps.
Note:
Before starting you will need a free license. You will get it from the Embarcadero website. Delphi Starter Edition is free for non-commercial purposes. If you sell a product developed in Delphi Community Edition, you will have to purchase a license as soon as your company gets $5000 in revenue, per year.
How to use the IDE
The IDE is pretty much self-explanatory. There are tooltips for all buttons/functions. It also has context-sensitive help. For example, if you open Project Options and press F1, the manual will show information about the currently open tab.
I highlighted in the screenshot below the most important parts:
The Project Manager
Here you add/remove files to/from your project.
You can also tell the compiler how to compile your application: debug/release mode or for which platform to compile (windows, mac, etc)
The Component Palette
Here you find the visual components that you will drag and drop onto your form
The Object Inspector
Here you set the visual and non-visual properties of visual components you placed on your form (GUI).
Component palette
It contains ALL visual or non-visual controls that you can drop on your form.
Compiler messages
Shows a list of hints, warnings or errors when you compile your program. It also shows the results of a “Find in files” search.
Form designer
This is where you build your GUI. We will study this in detail soon.
You can reconfigure all these windows to better fit your workflow/monitor size. If you have a dual-monitor system, you can leave your code designer on one monitor and move all the tool panels on the other monitor.
You have two main desktop layouts (but you can define more):
The Design layout (that you see in the screenshot below) where you write code and design your GUI
The Debug layout where you can easily access debugging tools/panels like breakpoints, variable watches, call stack, running threads, etc
The IDE will automatically switch from Design to Debug layout when you run your program.
Some useful Code Editor tips
While writing code you can press Ctrl+Space to invoke the Code Insight. Code Insight will show a list of possible instructions/variables/functions/etc that you can type in. Note: This won't work if you are in debug mode (the program is running). This is probably one of the most used keyboard shortcuts in Delphi. Remember it.
While you are inside of a function parameters (between parentheses) you can press Ctrl+Shift+Space to see the prototype of that function and the expected parameters.
You can select a rectangular block of code with the mouse while keeping the Alt key pressed. Press Tab to indent the whole block.
The CnPack plugin allows you to reorder (move up and down) lines of code with Ctrl+Alt+ArrowUp/Down
The Code Proofreader from GExperts plugin will automatically fix typos for you. For example, if you type by accident ‘beginn’ it will magically replace it with ‘begin’. You can train the plugin to do cool tricks. For example, you can type ‘bgnd’ and it will automatically replace it with ‘begin end;’ Believe me, that plugin will spoil you! I love it.
The Form Designer
The Form Designer allows you to build the GUI and easily write the code “under” your GUI. For example, create an empty form and drag and drop a button on it. Now just double click the button and the IDE will automatically switch from the Form Designer to Code Editor and it will add some code into your Pas file to handle the mouse click event. All you have to do now is to write the code you want to execute when the user clicks that button. For example, we could show a message when the user clicks the button. You only have to type ShowMessage(‘Bingo’). The IDE will do the rest for you: it will add another 5 lines of code and will link the button to the event handler.
If you are curious you can see the GUI code in the DFM file: right-click on the form and choose “View as text”. But you will never ever have to edit the DFM file manually (although you can). In “Your first Hello World application” chapter you will find the listing of a DFM file. As you will see, its format resembles a simplified XML so the file is easy to read by humans (I hope you are a human otherwise you will have problems understanding that file).
There is a huge advantage in keeping the GUI elements in a text (DFM) file: you can add it to your repository so you can track the changes (diff) that you or your team made to the GUI.
(Note: In MS Windows terminology all windows are called “windows” but under Delphi terminology, they are called “forms”).
Common IDE shortcuts
F9 → Run program (probably definitively the most used key in Delphi :)
Ctrl+F9 → Compile program
F11 → Show the Object Inspector
F12 → Show the Form Designer
Ctrl+F12 → Show the list of units used in the current project
Ctrl+Num+ → Increases the font size of the Code Editor. Use Num- to decrease font size
Ctrl+Shift+F11 → Show Project Options
Ctrl+F → Search
Ctrl+R → Replace
Ctrl+S → Save current file
Ctrl+Shift+S → Save all open files
In debug mode
Ctrl+F2 → Terminate program execution immediately
Breakpoint → Click the gutter (left side of the editor, next to the line numbers) to add a breakpoint.
Ctrl+F5 → Add the variable under the cursor to the ‘Watch list’ to monitor during debugging.
F8 → Step Over. Make the debugger to execute the next line of code. If the line contains a function, it executes the function (without stepping inside that function) and then stops at the next line of code after that function.
F7 → Same as F8 but steps into the functions.
You will find all IDE shortcuts here.
3rd party libraries
Installing a 3rd party library is as simple as dragging your file into the ‘Project Manager’ to add it to your current project (if it consists of a single file).
If your library has more than one file, or if you know that it is a library that you will use in all your projects, then you can add the path in “search paths”.
If the library was supposed to install a visual component into the IDE, you need to right-click it in the Project Manager, then choose “Install” from the pop-up menu.
A crash course in Pascal language
Before writing our first one-liner Delphi program to show a Hello message we need some basic knowledge of Pascal/Delphi.
This chapter will be probably the fastest crash course in a programming language you will take in your entire life.
I will assume that you have a very very basic understanding of programming languages so I won’t explain the most basic things such as what is a variable or a function.
If you already know C++
If you are coming from the C++ world, here is a quick overview of the main differences between Pascal and C:
C++ |
Pascal |
/* comment */ |
{ comment } |
{} |
begin end |
&& |
AND |
|| |
OR |
“string” |
‘string’ |
if (x>0) |
if x>0 |
void MyTest(void) |
procedure MyTest |
__ |
Not used |
* SomePointer |
SomePointer |
MyObject->method() |
MyObject.Method |
for (int n=0, n<11, n = n+1) |
for n:= 0 to 10 do |
You might also want to see this (a bit outdated but still good) C++ to Object Pascal comparison page and also this page (a bit more up to date).
Hold your breath. We will get started!
Comments
Statements below are comments:
{
This is a comment, usually for large blocks of code }
/* this is
also comment */
// this is a comment for one line of code
Assignments
In Delphi, we use : to declare a variable, := to assign something to it
var s: string; //we declare a variable s that will hold a string
s:= ‘Peekaboo’; // we assign a string to s
Then we use = for testing, in IF instructions:
if s = ‘Peekaboo’ // we test if s contains the ‘Peekaboo’ string
then ShowMessage(‘We found it!’)
else ShowMessage(‘Nope. It is not there.’);
Simple-type assignments
var x,y: integer;
x:= 0;
y:= 1;
x:= y; // value of y is copied into x. now x will be 1, y will be 1
y:= 77; // now x will be 1, y will be 77
String assignments
Under the hood strings are dynamic structures but they are not objects!
Assigning a string variable to another string variable will copy the content on the second variable into the first one:
var s1, s2: string;
s1:= 'apples';
s2:= 'oranges';
s1:= s2; // value of s2 is copied into s1. now s1 contains 'oranges'
s2:= 'metallic taste'; // now s1 contains 'oranges' while s2 has metallic taste
Note for geeks: In order to improve speed, Delphi uses Copy-on-write. This means that when you assign s2 to s1, Delphi will not necessary have two strings in memory, but only one. Later, if necessary, Delphi will create two strings.
Assignment for dynamic variables (and objects)
In Delphi, x and y are simply pointers:
var x, y: TObject;
We instantiate an actual object and assign it to x:
x:= TObject.Create;
and then we assign x to y
y:= x;
Now x and y will point to the same object (memory address). To oversimplify it, x and y are the same things (I know some people will cringe when they heard what I just said, but I want to make things easier for beginners).
Whatever change you apply on the x object will reflect also in y. For example, if you destroy the x object with FreeAndNil(x), the y variable will point to a garbage/deleted object. Don’t access y anymore from now on.
Constants
To declare constants we use:
Const
i = 2000; // Delphi will make an integer out of this
r = 3.14; // a float
s = ‘Magic’; // a string
b = true; // a boolean
As you can see you don’t have to declare the type. Delphi will find the appropriate type for your constant.
Note that we don't use := or : when we declare constants.
Procedures vs functions
All programming languages have functions but I don’t know any other language that has procedures (probably there are). A procedure is a function that returns nothing. In C++ you would call this a void function.
Why Pascal has procedures if you could just use a function that returns void/nothing? Returning something involves quite some overhead (restoring registers). Therefore, procedures are faster since they return nothing. Take advantage of this when you have a small function that you call it in a tight loop. Convert your function to a procedure and your loop will be significantly faster.
Variables
Global variables are always initialized to 0 (or whatever applies).
Local non reference-counted variables are NOT initialized.
Local reference-counted variables are always initialized to Nil or '' for strings.
The result of a function is uninitialized!
The fields of a class are always initialized to 0 (or whatever applies).
Note that before Delphi Rio, all variables had to be declared in an individual ‘var’ block, at the beginning of the function/procedure. Now, you can declare them anywhere, like in C++. Hmm… I am not sure I like that. It is definitively convenient to be able to declare a variable in the middle of a function without having to go to the beginning of the function (even though you can achieve automatic variable declaration with Ctrl+Shift+V). On the other hand, this will make the code look ‘dirty’.
Basic types
Some of the basic types in Delphi are:
Boolean: 1 byte
String: Managed string
Integer: 32 bit, signed (most ‘popular’)
Cardinal: 32 bit, unsigned
Shortint: 8 bit, signed
Smallint: 16 bit, signed
Byte: 8 bit, unsigned
Word: 16 bits, unsigned
Int64: 64 bit, signed
Single: Floating point , 4 bytes
Double: Floating point, 8 bytes
Extended: Floating point, 10 bytes
The compiler will prevent you from assigning incompatible types directly, like a Double to an Integer. To do so you need to tell the compiler that you know what you are doing:
var
i: integer;
d: double;
Begin
i:= 0;
d:= 3.14;
i:= d; won’t compile
i:= round(d); this will compile. The number will be rounded!
End;
Operators
+ |
Add two numbers |
- |
Subtract one number from another |
* |
Multiply two numbers |
/ |
Classic division operation. Returns a float. |
div |
Divide two numbers. Returns an integer. The number after the comma is simply truncated. |
mod |
The remainder from dividing one integer by another |
Statement separator
Notice the semicolon (;) at the end of each Delphi instruction. It is mandatory! In Delphi, all complete instructions need to end with a semicolon.
This is pretty much like in C++ but with two differences (only one important): an If/Then/Else is considered to be a single line instruction. So, you only put a semi-column after else, while in C you will put also semicolon after then:
if x> 0
then inc(x)
else dec(x);
Strings
Strings can be concatenated like this: s1+s2. Example:
var
s1, s2: string;
sFinal: string;
begin
s1:= ‘Delphi’;
s2:= ‘ is fast’;
sFinal:= s1+s2;
print(sFinal); // This will print ‘Delphi is fast’
end;
Mixing strings and numbers:
var i: integer;
var s: string;
Begin
i:= 4;
// we use IntToStr to convert an integer to a string
s:= ‘Dephi is ’+ IntToStr(i)+ ‘ times faster than C#’;
print(s); // this will print ‘Delphi is 4 times faster than C#’
End;
Note that strings are enclosed by single quotes. If you want to put a single quote in a string you have to escape it (add it twice):
s:= ‘Dad don’’t shoot. It’’s me!’;
print(s); // This will print: Dad don’t shoot. It’s me!
This is the only exception when you need to escape things. Nothing else needs to be escaped in a Delphi string.
String implementation details (read-only if you want to be smart)
How Delphi achieve faster, safer strings? With compiler magic. Delphi strings are special structures. Over-simplifying the answer, let’s say that at the beginning of the string Delphi inserts a number which holds the length of the string and another number that holds the reference count (the reference count shows how many other variables are pointing to this particular string). These numbers are “secretly” inserted at position 0 (in Delphi strings are indexed in 1) so you don’t actually see them in your strings. This means that in reality, Delphi strings are a bit longer than they seem to be and they have some magic data at the beginning, data that you cannot see or access. The compiler manipulates that information behind the curtains. Actually, there are tricks to access those secret numbers but better don’t mess us with that.
This “secret” internal data is what makes the Delphi strings safe: if you attempt to write after the end of the string the Runtime range checking routines will catch you (and cut your fingers for trying to do it).
If you are interested in all the gory details about how strings are implemented in Delphi check this page.
Note: Under VCL, strings are “naturally” indexed. This means they are indexed in 1. Accessing element zero (s[0]) of a string will get you in trouble! If you are coming from C++ you will mistreat strings heavily until you get used to it.
String conversion routines
StrToInt – Tries to converts a string to an integer. Raises an exception if the string does not contain a valid number.
StrToIntDef - Fault-tolerant version of StrToInt. It does not raise an exception.
StrToFloat - Converts a string to a decimal number
IntToStr – Converts integer to string
Displaying a message
In this book, I will sometimes use the print procedure that does not exist. Whenever you see 'print' it means that you can use any of the below-specified methods to show a string on the screen. Your choice.
In a CLI application, we can use Write or WriteLn (which is Write + new Line) to print a string on the screen.
In a GUI application, we can use ShowMessage. We could also show the string in form's caption with Caption:= 'my string'. Another method (this will make the previous strings to remain on-screen) is to drop on a form a TMemo and write text in it, line by line, with: Memo1.Lines.Add(s);
For the sake of the exercise, let implement a print procedure that can receive an integer or a string as parameter:
procedure Print(s: string); overload;
begin
Caption:= s;
end;
procedure Print(i: integer); overload;
begin
Caption:= IntToStr(i);
end;
I will let you implement a procedure that can receive a float as parameter.
Enumerations
We can declare our own enumerations, sub-ranges, and sets. Example of enumeration:
Type
TDaysOfTheWeek = (Mo, Tu, We, Th, Fr, Sa, Su); // We declare a new type
Then we use it like this:
Var
Today : TDaysOfTheWeek; // Declare variable
begin
Today := Mo; // Assign a day to the Today variable
end;
Records
In Delphi you can declare a record (data structure) like this:
Type
RSoldier = record
Life: integer;
Ammo: byte; // He can only carry 255 bullets
Dead: Boolean;
Rank: string;
End;
Now we declare a variable of RSoldier type, and we initialize the fields:
var Ryan: RSoldier;
Begin
Ryan.Life:= 12;
Ryan.Amo:= 0; // man, you are screwed!
Ryan.Dead:= false; // not dead… yet
Ryan.Rank:= ‘Private’;
…
Hint for C++ users: In Delphi, you can return a record from a function!
With
the “with” instruction is typical to Pascal. There is no equivalent in C++.
Using “with”, we can rewrite the above code as:
var Ryan: RSoldier;
Begin
with Ryan do
begin
Life:= 12;
Ammo:= 0; // man, you are screwed!
HP:= 20;
Dead:= false; // not dead… yet
Rank:= ‘Private’;
End;
…
You can also use with on objects.
Arrays
In Delphi, you can have static and dynamic arrays.
Static arrays have a fixed size, size that is known at design time. You cannot resize them.
Static arrays
Static arrays are very convenient to work with because the length of the array is known at compile time. You don't have to initialize them. You can immediately use them at runtime.
Example of static arrays:
var ai: array [0..99] of integer; // this array is indexed in 0
var ar: array [4..6] of real; // this array is indexed in 4 and has three elements
Ai has 100 elements. Let’s put something into the first element:
ai[0]:= 42; // 42 is the answer to everything
Dynamic arrays
The size of a dynamic array is not known at compile time. In order to use it at runtime, we need to state its length (allocate memory for it). If we forget to do it we will get a Range Check Error.
var ad: array of integer; //undefined size
We just declared a dynamic array but we cannot just use it immediately.
First, we need to allocate memory for it:
SetLength(ad, 100);
At this point, the array has 100 elements. Note that none of those 100 elements have been initialized to zero; they contain garbage (random data).
Now that the array has a size, we can read/write its elements. Dynamic arrays are always indexed in 0. So, if we want to write number 42 in the first element we do this:
ad[0]:= 42;
Let’s put number 42 also into the last element:
ad[99]:= 42
or better:
ad[Lenght(ad)-1]:= 42;
Length(ad) returns the total number of elements in the array (100 in this case). But the dynamic arrays are indexed in zero so we have to subtract one.
Instead of Length we can use High. High returns the position of the last element (99) of array:
ad[high(ad)]:= 42;
Need to make the array larger? You can resize your array anytime you want. Let’s say we want to make the array 10000000000 elements big:
SetLength(ad, 10000000000); //wow! do we have enough RAM for that?
After resizing the array, the values we already stored in the first 100 elements will be preserved, but the new elements we just added will not be initialized (will contain random data).
FOR loops
For loops are very simple in Pascal. Let’s execute a procedure 100 times:
var i: integer;
for i:= 0 to 99 do SomeProcedure;
for i:= 1 to 100 do SomeProcedure; // this will do it also :)
If we want to show all characters in a string, one by one, we do:
var i: integer;
str: string
Begin
str:= ‘Delphi’;
for i:= 1 to Length(str) do // Under VCL, strings are indexed in 1
print( str[i] );
End;
Or even cooler:
var
c: char;
str: string
Begin
str:= ‘Delphi’;
for c in str do // this way we don’t have to remember that VCL strings are indexed in 1.
ShowMessage(c);
End;
A more complex example:
program LoopThroughArray;
const
// We declare a constant called arr. It is an array of 3 elements
//
Then we initialize the elements with 3 float values (1, 1.1 and 1.2)
arr : array[1..3] of real = ( 1, 1.1, 1.2 );
var R: real;
begin
for R in arr do
Print( R ); // This will print 1.0, 1.1, 1.2
end.
You can Break, Exit and Continue a loop. You can also have a reverse loop:
for i := 100 downto 1 do Print(i);
Important!
Pascal “for” loops are very fast because they use a thingy called induction variables. But it but comes with a drawback. You cannot read the ‘i’ variable once the loop is done:
var
i: integer;
for i:= 0 to 99 do
print(i); // this might print any number,
// not necessarily 99 as you would expect.
You have been warned!
Pointers
Pointers in Delphi are type-safe, which means that a pointer of a data type can only be assigned to a pointer of the same data type. Pointers can never be assigned to non-pointer variables.
Pointer arithmetic is also possible.
Naming conventions
Delphi language does not use Hungarian notation and it doesn’t really enforce any rules on how to name your functions and variables – even though there is a short guideline on naming conventions.
You don’t need Hungarians anyway as the IDE helps you figure out which variable is what type and where it was declared: just hold your mouse over a variable and a tooltip will pop-up to tell you the type of that variable and its location. Note: tragically, this feature does not exist under the C++ Builder personality. This is probably because of the way how variables are declared in C++.
Another trick to quickly inspect your function/variables is to Ctrl+click them. The IDE will take you to the place where that variable/function was declared. Once you learn this trick you will use it 200 times per day.
There is one notation you will constantly see in Delphi code: all classes start with T: TObject, TButton, TForm, TList, etc. Even though this is not enforced, I strongly advise you to use it.
Delphi does not require to put parenthesis () after each function. But if you are used to the C++/Java world, you can do it. The compiler will ignore you.
Other notable rules:
Delphi does not allow a variable name to start with a number.
Delphi is not case sensitive. So a variable (or function) declared as s: string can be later written as ‘S’ (capital S). ‘S’ and ‘s’ are the same thing!
There are only two (special) exceptions when Delphi is case sensitive: one is the Register procedure, the other case is the imports of external functions (from DLLs for example).
You cannot use ‘Result’ as a variable name. ‘Result’ is a reserved word (to be used inside functions).
Fields of a class (private variables) start with ‘F’
Function overloading
In Delphi, you can have two procedures/functions with the same name but different parameters/return types. This is called overloading. Here is an example:
procedure SavePrivate(Name: string); overload;
procedure SavePrivate(Name: string; MaxSearchOpDuration: integer); overload;
If in your program you call SavePrivate(Ryan), the compiler will use the first procedure. If you call SavePrivate(Ryan, 7) the compiler will use the second procedure.
Parameters of a function
You can pass parameters to a function/procedure:
By reference
procedure CutString(var s: string);
You can modify the s inside the procedure. The caller will see the modified content of s.
Delphi dynamic objects are ALWAYS passed by reference.
By value
procedure CutString(s: string);
You can modify the s inside the procedure BUT the caller will not see the modified version of s. To achieve this, an internal copy will be made for s. The internal copy is disposed of once the procedure exits.
By constant value (fastest)
procedure CutString(const s: string);
You can read s inside the procedure, but you cannot modify it. Much faster because no copy of s will be made internally. Use this whenever you can.
Out parameters
You use ‘Out’ to indicate that the parameter of a function/procedure is an output parameter (as opposed to the input parameter).
When you see an ‘Out’ parameter you know that no matter what values you pass to that ‘out’ parameter, it will be ignored (the function will never read that parameter). The function will use that parameter only for output.
procedure CutStringInTwo(InputStr: string; out Piece1, Piece2: string);
In this example, CutStringInTwo will take the InputStr parameter, cut it in half and output the two resulted parts in Piece1/Piece2.
Note: Instead of ‘out’ you can simply use a ‘var’ parameter. It has more or less the same effect. But by using ‘out’ you clearly indicate your intention. I told you Delphi is a clean language!
Default parameters
A cool trick you can do with the parameters of a procedure/function is to declare them “initialize” them to a certain value.
For example:
procedure SavePrivate(Name: string = 'Ryan');
begin
Print(Name);
end;
You can then call the procedure without any parameters. Delphi will automatically pass 'Ryan' as a parameter for this function.
SavePrivate; // This will print 'Ryan'
Result of a function
You can set the result of a function using the keyword Result. Unlike C++ this will not cause the function to exit. Obviously. you cannot use 'Result inside of a procedure.
function CanWeFindTheWayBackHome(WeAreLost: boolean): boolean;
begin
if WeAreLost
then Return:= false
else
begin
ShowMessage(‘Yey! We go home!’);
Result:= true; // This will not exit the procedure. So, ComputeHomePath will still be executed.
end;
ComputeHomePath;
end;
We can use Exit(True) to return true and immediately exit the function. In this case, ComputeHomePath will not be executed.
Generics
Delphi generics are equivalent to the C++ templates. Here is an example in which we add some integer numbers to a list. Note, that instead of <integer> you can use anything else, like <real>, or even classes:
var List: TList<Integer>;
begin
List := TList<Integer>.Create; // Create the list
try
List.Add(100); // add an integer
List.Add(200); // and another one
finally
FreeAndNil(List);
end;
end;
Classes
Constructors/destructors
The most basic class in Delphi is TObject. Everything else inherits from TObject.
Let's build our own class:
Type
TMyClass = class(TObject)
private
PrivateField: integer;
public
constructor Create(i: integer);
destructor Destroy;
end;
constructor TMyClass.Create(i: integer);
begin
inherited Create; // You must always put Create on the first line!
PrivateField:= I;
end;
destructor TMyClass.Destroy;
begin
Print('We go bye-bye');
inherited Destroy; // You must put Destroy always on the last line!
end;
Highlight this with a fluorescent marker because it is important: when you override parent’s default constructor (TObject in this case), you must always call inherited on the first line. In the destructor, you must always call inherited on the last line.
Inherited ensures that the parent’s default constructor/destructor is always called.
Your computer will not blow up if you forget to call inherited, but for sure your program will crash with some hard-to-detect errors.
Published properties
Delphi classes can have private, protected and public members (methods) like in any other programming language. However, Delphi’s visual components also have published methods. Published is like public: anyone can access those methods). What they have extra is that they are also exposed visually. So, published is related to VCL.
For example, you can create your own custom visual component: let’s say a button that you want to make it red. By default, TButton does not have a property Color.
You can make your own method. You drop your new TButton on a form and... nothing special happens.
Type
TMyNewButton= class(TButton)
Private
FColor: TColor; // naming convention: fields always start with F
Public
Property Color: TColor read FColor write FColor default clRed;
end;
However, if you make this method published, it will appear in the IDE into the Object Inspector so you can change the color of the button at design time (and run time).
All published properties used to change properties of visual components are called... properties. Here is a (simplified) example:
Type
TMyNewButton= class(TButton)
...
Published
Property Color: TColor read FColor write FColor default clRed;
end;
The ubiquitous Sender parameter
One interesting thing you will encounter everywhere in Delphi is that all event handlers (the code that responds to user actions, like mouse clicks) have a variable called 'Sender' as a parameter. You can test this by starting a new application. In the main form, drop a button. Its implicit name will be Button1. Now, double click this button and the IDE will automatically write this code for you:
procedure TMainForm.Button1Click(Sender: TObject);
begin
end;
So, what is this 'sender' parameter? This parameter is the actual GUI element that generated the event. In the above example, if you run the application and click the Button1, the Button1Click event handler will be called. The sender will be Button1.
Let's actually see this:
procedure TMainForm.Button1Click(Sender: TObject);
begin
Print( (Sender as TControl).Name);
end;
The code above will first typecast Sender to TControl. We need this because TObject doesn't have a 'Name' property. But TControl it does. So, after the typecast we interrogate the name of the Sender.
The procedure will print on-screen 'Button1'. We just demonstrated that the Sender is actually the button that the user just clicked (Button1).
How is this useful? I think it is obvious now: When you are inside of an event handler you can interrogate Sender to see which GUI element was clicked by the user, to generate that event.
For example, let’s say that you want to create a Windows Calculator clone. You place 10 buttons with numbers from 0 to 9 on a form and you set their caption to numbers from 0 to 9. Now when the user clicks a button, you want to display the button that the user clicked. Instead of creating one unique event handler for each button (a total of 10) you can create one single event handler and assign it to ALL 10 buttons:
Procedure TMainForm.OnAnyButtonClick(Sender: TObject);
var s: string;
Begin
s:= (Sender as TButton).Caption; // first we typecast then read the caption
Print(s);
End;
Try/Finally
C++ users are used with try/catch. In Delphi try/catch is called Try/Except. And it does the same thing as in C++: the piece of code you put in Except will execute ONLY in case some error happens.
However, Delphi has also Try/Finally. Code in Finally is executed regardless of what happens in the Try statement.
Try/Finally is used when some cleanup needs to take place, such as freeing resources. So, Try/Finally is similar to auto pointers in C++. But it can do more.
The main difference between Try/Except and Try/Finally is that the code in Except executes only if an error appears, while the code in Finally executes always: when an error appears and also when no error appears.
The procedure below will create an object:
Procedure CreateAButton;
var
myButton : TButton;
Begin
myButton:= TButton.Create;
myButton.DoSomething; // This might crash and burn
FreeAndNil(myButton); // myButton might never be released
End;
If DoSomething raises an exception, the program immediately exits the CreateAButton procedure, without leaving us the chance to free the object. We will leak some memory.
Now, we will see the same code, but with a significant improvement: we enclose code that might fail (DoSomething) in a Try/Finally block. This guarantees that your object will be freed even if DoSomething fails.
Procedure CreateObj;
var
myObj : TObject;
Begin
myObj := TObject.Create;
Try
myObj.DoSomething; // This might crash and burn
Finally
FreeAndNil(myButton); // myButton released, no matter what!
End;
End;
Important:
Resource allocation (object creation, in the above example) always needs to take place BEFORE Try.
Always use Try/Finally when you allocate resources (memory) to make sure you always release them. You can use Try/Finally also to gracefully terminate operations when you are done (like closing files or DB connections).
Load a text file from disk
This is a quick and dirty way to read a text file from disk:
var TSL: TStringList; // TStringList is a class that holds a list of strings
begin
// We create the object and assign it to TSL
TSL:= TStringList.Create;
// Load the text file into the object
TSL.LoadFromFile(‘c:\my file.txt’);
// Show content of the whole file
ShowMessage(TSL.Text);
// Modify the first line
TSL.Lines[0]:= 'Some new text for the first line';
// Now, save the whole file to disk
TSL.SaveToFile('c:\my file.txt');
// Bye-bye object! We don’t need you anymore.
FreeAndNil(TSL);
end;
Bonus: You can do TSL.Sort to sort the lines alphabetically.
Summary
C++ and Pascal are both low-level, high-speed languages. However, the similarities stop here. There are totally different languages.
Inheritance, polymorphism and typecasting
Inheritance chain
I mentioned before that in Delphi all objects have TObject as an ancestor. Write this into a new Pas file, to see it with our own eyes:
var
aButton: TButon;
Now Ctrl+Click the TButton keyword. The IDE will take us to the declaration of TButton. We see that it inherits from TCustomButton. If we continue digging deeper (Ctrl+Click) we see that TCustomButton inherits from TButtonControl and so on, until we end up to the most basic class: TObject.
Here is the full inheritance chain:
TObject -> TPersistent TComponent TControl TWinControl TButtonControl TCustomButton TButton
If you do this for any class in Delphi, you will always end up to TObject.
Visual components
If you look down the inheritance chain, all controls up to TComponent are visual. This means that they have the ability to appear on an IDE palette and be manipulated in the Form Designer and you can drop them on a form.
Another characteristic of TComponent is 'ownership'. This is the ability to manage other visual components. If component A owns component B, then A is responsible for destroying B when A is destroyed.
Typecasting
Ok. We go into more complex programming stuff. If you think is too complex, think that you only have one typecast in Delphi while in C++ you have four. So, could be much worst. Now that I just made your day a bit brighter let dive in.
There is a very special feature emerging from the fact that all Delphi classes inherit vertically from TObject. It means that we can safely use a descendant class where an ancestor class is expected.
Let's say we have a base class called TMachinery (reminder: in Delphi all classes start with T). We derive from TMachinery another two classes called TCar and TAirplane. We will typecast back and forth between these classes and see what happens. The code is below. Just open a new Delphi or Lazarus unit and replace the code in it with the code below.
By the way: I used Lazarus to test the code below. But the code also compiles on Delphi. This is a great example that shows how closely related Delphi and Lazarus are.
unit uMachines;
{$mode objfpc}{$H+} // Remove this line if you compile under Delphi.
interface
uses
Classes, SysUtils;
type
TMachinery = class(TObject)
public
Name: string;
constructor Create; overload;
function WhatAreYou: string;
end;
TCar = class(TMachinery)
public
constructor Create; overload;
function WhatAreYou: string;
end;
TAirplane = class(TMachinery)
public
constructor Create; overload;
function WhatAreYou: string;
end;
implementation
constructor TMachinery.Create;
begin
inherited Create; // we call parent's constructor
Name:= ' Some kind of machine';
end;
function TMachinery.WhatAreYou: string;
begin
Result:= ' I''m a machine';
end;
constructor TCar.Create;
begin
inherited Create; // This will call the parent (TMachinery) constructor and set the Name to 'some kind of machine'...
Name:= ' a car'; // ...however, here we override the name with our own string ('a car')
end;
function TCar.WhatAreYou: string;
begin
Result:= ' I''m a car';
end;
constructor TAirplane.Create;
begin
inherited Create; // same here
Name:= ' an airplane';
end;
function TAirplane.WhatAreYou: string;
begin
Result:= ' I''m a bird';
end;
end.
The hard typecast
TMachinery(Car).WhatAreYou;
This applies a hard typecast over a variable. When you do so, you're telling the compiler that you know what you're doing and the compiler trusts you. The compiler won't stop you even if the typecast is wrong (this can result in violations). Always try to avoid hard typecasts.
The 'as' typecast
It is considered better programming practice to use the 'as' keyword instead of a hard typecast.
(Car as TMachinery).WhatAreYou;
The cast will be done only if Car is a TMachinery. For example (Car as TButton).WhatAreYou won't compile.
The 'is' operator
We use 'is' to ask if we can do the cast. Example:
if SomeObject is TAirplane
then (SomeObject as TAirplane).Announce('Cabin crew prepare for landing');
Note: Here we can safely use a hard typecast because we know that the typecast will work (we just tested that with 'is'):
if SomeObject is TAirplane
then TAirplane(SomeObject).Announce('Cabin crew prepare for landing');
Testing the code above
Start a new Delphi VCL/Lazarus project with an empty form and paste the code below in it (replace the code that is already there). You will need to also put on your form a memo and two buttons (rename the buttons to btnPolymorphism and btnTypeCast). After you paste the code, double click the buttons and the form and the IDE will automatically link the button's click event handler to the code below (btnPolymorphismClick and btnTypeCastClick).
unit MainForm;
{$mode objfpc}{$H+} // Remove this line if you compile under Delphi.
interface
uses
Classes, SysUtils, Forms, Controls, StdCtrls,
uMachines; // here we link to the unit where the ‘machines’ are defined.
type
TForm1 = class(TForm)
btnPolymorphism: TButton;
btnTypeCast: TButton;
Memo: TMemo;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnPolymorphismClick(Sender: TObject);
procedure btnTypeCastClick(Sender: TObject);
end;
var
Form1: TForm1;
Car: TCar;
Airplane: TAirplane;
Machinery: TMachinery;
implementation
{$R *.lfm} // use *.dfm if you compile this in Delphi
// This procedures executes everytime the program starts
procedure TForm1.FormCreate(Sender: TObject);
begin
// We create the objects we need
Machinery:= TMachinery.Create;
Car:= TCar.Create;
Airplane:= TAirplane.Create;
end;
// This procedure executes every time the program shuts down
procedure TForm1.FormDestroy(Sender: TObject);
begin
// We free the objects
FreeAndNil(Airplane);
FreeAndNil(Car);
FreeAndNil(Machinery);
end;
procedure TForm1.btnTypeCastClick(Sender: TObject);
begin
Print('');
Print('[Valid typecasts]');
// Pointless typecast. This will show 'some kind of machine'
Print((Machinery as TMachinery).Name);
// This will show 'a car'
Print((Car as TMachinery).Name);
// This will show 'an airplane'
Print((Airplane as TMachinery).Name);
// Impossible typecast. This line won't compile
Print((TMachinery as Airplane).Name);
Print('');
Print('[Testing object type]');
if Car is TMachinery
then Print(' The car is a machine') // This will always execute
else Print(' It is not?'); // This will never execute
// Impossible typecast. This line won't compile
// The compiler already knows that this cannot be, so it won't even try to compile this code. Compiler error message: Class or Object types "TCar" and "TAirplane" are not related
if Car is TAirplane
then Print('The car is an airplane')
else Print('Nope. The car is not an airplane');
Print('');
Print('[Valid but unsafe typecasts. Use ''as'' instead.]');
// This will show 'a car'
Print(TMachinery(Car).Name);
// This will show 'some kind of machine'
Print(TCar(Machinery).Name);
// This will show 'an airplane'
Print(TMachinery(Airplane).Name);
// This will show 'some kind of machine'
Print(TAirplane(Machinery).Name);
// Invalid typecast
// With this typecast you can force compiler's had to compile it even if it is unsafe.
// But the compiler will warn you anyway: Warning: Class types "TCar" and "TAirplane" are not related
Print('Invalid typecast: '+ TAirplane(Car).Name); // This will show 'a car'
// Impossible typecast. This line won't compile
// The compiler will refuse to compile this buggy code
// and will show an error: Class or Object types "TAirplane" and "TCar" are not related
//Print((anAirplane as TCar).Name);
end;
procedure TForm1.btnPolymorphismClick(Sender: TObject);
begin
Print('');
Print('[Polymorphism: Hardcast]');
//Some calls will show a false answer because of the hard typecast
// This will show 'I'm a bird'.
Print(TAirplane(Machinery).WhatAreYou);
// This will show 'I'm a car'
Print(TCar(Machinery).WhatAreYou);
// This will show 'I'm a machine' however it is a lie because the typecast is invalid!
Print(TMachinery(Airplane).WhatAreYou);
// This will show 'I'm a machine' however it is a lie!
Print(TMachinery(Car).WhatAreYou);
Print('');
Print('[Polymorphism (Safecast)]');
// This will show 'I'm a machine'
Print((Airplane as TMachinery).WhatAreYou);
// This will show 'I'm a machine'
Print((Car as TMachinery).WhatAreYou);
// This will raise an EInvalidCast error at run time
//Print((Machinery as TAirplane).WhatAreYou);
// This will raise an EInvalidCast error at run time
//Print((Machinery as TCar).WhatAreYou);
end;
end.
As you can see you can only typecast 'downstream' from a child to a parent because the child has all about its parent. You cannot (safely) typecast 'upstream', a parent to a child: Machinery as TAirplane. The parent class (TMachinery) knows everything about its parent (TObject) but knows nothing about the children classes inherited from itself.
How would TMachinery possible know about TAirplane if TAirplane would have been implemented in a different file?
The best example to visualize why typecasting works only one way, is to imagine that you are a factory that makes bricks. You know everything about the suppliers from where you buy your materials, but you cannot know who buys and where your bricks are used.
Additional notes:
We cannot typecast also between 'brothers': Car to Airplane (or the reverse).
All the above behavior is based on RTTI (runtime type information). All classes in Delphi have full RTTI support.
It is strongly recommended that whenever possible to use polymorphism instead of RTTI.
Summary
Never use a hard cast. Always use the safe 'as' cast.
Your first “Hello world” application
We are getting closer to the end of this book and it is time to get our hands dirty.
For your first application, just create a new empty project by clicking the “File ->New->VCL form application” menu.
You can also click the “Multi-device application” menu if you want an FMX application.
I find the VCL easier to use, but in the end, it doesn’t matter what you choose - the tutorial/code will be the same.
An empty form will be automatically created for your new application:
Hint: Press F12 to show the form if it is not visible (F12 will switch back and forth between the Code Editor and the Form Designer).
Press F11 to show the Object Inspector. Look at the properties of the form. Be bold and change some of those properties even if you don't know exactly what you are doing. If you screw up, you can start a new project:
You can set the 'Caption' to say “Good boy” for example. You can load an icon, by clicking the “Icon” property.
You can set the AlphaBlend to true and AlphaBlendValue to 200 to make the form a bit transparent.
You can set the “Color” property of the form to pink or
other jazzy/eye-hurting colors. You can set ScreenSnap to true to
make the form snap to the screen edges.
Boy! You can have lots
of fun. Just you and the Object Inspector.
Properties of a VCL/CLX form
Actually, I promise that in your first 2-3 days of Delphi you will play with the Object Inspector as when you were 12 and received that cool RC toy car for Christmas.
Now let's continue... In the “Components” palette just locate the TButton component. There are LOTS of visual components there, so you better use the ‘search’ box to locate your TButton.
Once located, drag it on to the form.
Now we want to display a dialog box that will show the “hello world” message when the user clicks the button. For this, double click the button. Delphi will automatically hide the Form Designer and show the Code Editor. It will also automatically write some code to assign an event handler to the OnClick event of that button, and it will put the cursor inside the event handler so you can just write the following line of code:
ShowMessage('Hello world!');
Below is a listing of your program. It has a total of 25 lines, but all the code you see there, except the ShowMessage('Hello world!') line is actually written by the IDE.
The three blue dots in the gutter indicate that only three lines of code will actually be compiled.
To run the program click the Run button (green triangle) in the toolbar or press F9 and your program will appear on the screen, in all its glory:
Look around and if there is nobody in the room, say “Heellooo beauty” to your first one-liner Delphi GUI application.
A short analysis of a Delphi unit
If we take a short look at the PAS file (PAS files are also called units) we will see that it is split into two sections: the interface and the implementation.
The interface is the section where you declare stuff (for C++ users, this is equivalent to the header file) while the implementation is the part where you… well… implement the function that you just declared in the interface.
In uses, you declare other Pascal files (units) that you need to link to. For GUI elements, you don’t need to manually add units to the uses list. The IDE will automatically do it. For example, if you drop a button on your form, the IDE will automatically add the unit where TButton is declared (StdCtrls) to the list of used units.
All Pascal files start with the keyword unit and terminate with end. The name of the unit MUST be identical with the name of the Pascal file containing the unit. In our case, the unit name is MainForm so the file must also be called MainForm.Pas. This means that if you MANUALLY (outside of the IDE) rename your files, you need to open the file in a text editor to update the first line (the unit line). Well, you will never have to manually rename your files in this way... but it is good to know. I strongly recommend you never ever do operation on your Delphi projects outside the IDE.
The
{$R *.dfm} is a compiler directive (I know it looks odd) that tells
the compiler that for this pas file there is also a DFM file that
needs to be included.
$ stands for compiler directive and the R
stands for Resource (DFM resource in this case). Basically, this is
how the IDE and the compiler know how to link together the PAS and
the DFM file.
Tips for geeks:
There are another two optional sections: initialization and finalization.
In ‘initialization’ you can put code that you want to be automatically executed by your app when the unit is loaded into memory. For example, you could set a variable to a specific value; you could open a DB connection; check if some flags are set into the system, etc.
Smallest Delphi application
I want to mention here that the code for the smallest Hello World Delphi application is as follow:
program HelloWorld; // Windows only
{$APPTYPE CONSOLE}
begin
Write('Hello World');
end.
program CrossPlatformHelloWorld; // All platforms
uses FMX.Dialogs;
{$R *.res}
begin
ShowMessage('Hello world!');
end.
Summary
You need fewer minutes to design, compile and run a simple Windows application.
You only need to write a single line of code for a GUI “hello world” app.
Debugging
If you are running your application from inside the IDE, whenever your code crashes, the integrated debugger will step in and it will ask you if you want to continue execution or if you want to debug.
If you choose to debug, it will put the cursor on the line of code that generated the fault. In most cases, this should be enough to help you figure out which is the problem. However, if this is not enough, you can look at the call stack:
The call stack
The call stack (aka stack trace) is the path of the execution of the program through your code, from program start to current execution point.
In the screenshot above, the blue arrow indicates that the program execution was halted by the debugger at the lines of code that generated the fault; which in this case, is in the DownloadImages procedure.
The execution path was:
The user clicked the Start button on the GUI. We know this because we see that the first procedure called is a procedure called actStartExecute that responds to the user’s action when the user presses the Start button (we assigned actStartExecute to the start button at design time).
The actStartExecute called Start...
which called Resume,
which finally called DownloadImages.
In the ‘Call stack’ window double click the DownloadImages procedure (or other procedures listed there). This will take you to the place where that procedure is implemented.
Preparing your project for debugging
The debugger runs out of the box. You don’t need to set any additional flags. However, you need to tell the compiler to insert more debugging info into your exe file. This will allow the debugger to show more information about the program your debug.
So, in order to be able to use the debugger in all its glory, you need to flip a few switches every time you create a new project. Don’t worry, this can be done in no time.
First, set the Compiler Options for 'Debug configuration':
Then set the Compiler Options for 'Release configuration'. (We will talk about 'configurations' soon).
Next, in the “Linking” page you need to set the Map File to Detailed. This allows the compiler to match the binary code with the source code. Every time you have an access violation in your code, the debugger will know which line of source code correspond to binary code that caused the crash.
Leave the “debug information” unchecked. Don’t let the name fool you – that’s for remote debugging, so you won’t need it (usually):
The settings for his page are the same even if you are in Release or Debug configuration (we will see one page below what a ‘configuration’ is).
Configurations
Note that (by default) your program can be compiled in two modes/configurations: debug and release.
When you build the program in Debug mode your compiler will put extra code to have all those nice debugging features and the Runtime Checking enabled. This will make the program a bit bigger and significantly slower.
Therefore, once you are ready to release the program on your website, you might want (it is not mandatory) recompile in Release mode. In release more you put the switches in the opposite way from the one shown in the screenshot above. This will make the program a bit smaller and 50% faster because “Optimization” is not on and those expensive Runtime Checks are not enabled anymore.
Note: One important thing to keep in mind: always keep “Optimization” disabled in Debug mode. If you forget to disable it, you won’t be able to inspect the variables in your program.
PreRelease configuration
By default, all Delphi projects come with two configurations: Debug and Release. However, you can define your own configurations. For example, I always have three configurations for my projects: Debug, Release and PreRelease.
Once your program is ready to be shipped (for the first time) to the customer, you can compile the program in PreRelease. PreRelease is the same as Debug but has Optimization enabled. The program should be a bit slower because you have all those debugging and Runtime Error Checking enabled, but Optimization will compensate (quite a bit) for that.
So, in PreRelease mode, your program speed is average while you still have Runtime Error Checking enabled, which will help you receive bug reports for your fresh-new-released program. If you receive bug reports, you fix them (preferably on the same day) and release updates. Once the bug reports stopped pouring in, you can switch your program to Release mode where you get maximum speed (but no Runtime Error Checking).
Hint for geeks:
Use the ‘Run->Pause’ menu to pause the execution of the program at any point in time. While paused, you can inspect the call stack, the variable in the program, etc. This is useful when your program stays too long in a procedure (freezes). You can find out which that procedure is, and why it stays that long in there.
How to use the debugger:
There are many tricks that you can do with your debugger but the most useful ones are:
Variable
inspection
Set
a breakpoint
into your program. When the program hits that line of code and
halts, all variables available at the current execution point and
all global variables/objects are ready for inspection, which means
that you can see or even
modify
their value. When ready, you and continue the execution of the
program. Your program will continue to run, behaving in concordance
with the changes you made.
Expression
evaluation
While the program execution is paused (due to a
breakpoint) you can type in code, that the debugger will execute.
For example, if you realize at that point that due to a bug your
code is not calling a function, you can call it right away and then
resume the execution of the program.
Relocatable
execution point
While the program execution is paused (due to a
breakpoint) you can simply indicate to the debugger which will be
the new line of code from which you want to resume the execution.
This slows you to pause the execution of your program, run the
program line by line until you find the code that is buggy. Once you
have found the bug, you might want to resume the execution of the
program but skipping the buggy line.
You will find a nice primer (with screenshots) in debugging here. The Delphi official manual for debugging is here.
Deploying your application to other platforms
Deploying your application on Android straight-forward. For example, all you have to do to run your FMX application on Android is to hook your Android phone/tablet to your computer via USB, compile for Android (your Android device will be shown in the list of devices) and… that’s it. You press F9 and the app is running there. You can even use an emulator. Cool!
However, Apple in its great wisdom makes the process more complicated. First of all, their EULA will only allow you to compile Mac programs on Mac hardware. Yes. You read it right!
So, stop reading now, go grab your wallet and run to the closest Apple store, just over the corner, and buy a not-so-cheap Mac computer. And also maybe some iPhones.
You will also need an Apple Developer Account – for this your wallet needs to have $99 in it. Per year.
Since Delphi is not allowed to compile the code on your PC, you need to install an application called Platform Assist Server (PAServer.exe; it comes with Delphi) on your Mac. The IDE will use this server to be able to compile and sign your code on Mac. There is a nice video from Embarcadero that explains how to use PAServer on Mac.
Linux also requires you to install PAServer. However, you can compile on Linux without the need for a developer account, on any standard PC hardware. (Well, Mac is also an Intel-based PC basically. They just like to pretend they are special).
Summary
The debugger is reliable and helpful.
For maximum debug commodity, set the compiler to Debug mode when you design, build and test an application.
For maximum performance, set the compiler to Release mode when you release the application to your customers.
Delphi makes development for Windows, Android, Linux easy.
Apple uses a smart business model to milk money.
Apple users love Apple because have lots of money -> go get them.
Apple hates developers.
Where do you go from here?
“All good things must come to an end” Geoffrey Chaucer said once. I hope you enjoyed the book. I really strived to condensate as much information in one small book.
As stated in the beginning, the book does not aim to teach you the depths of the Pascal programming language (or C++). However, at this point, you should have acquired enough knowledge (and enthusiasm) to get you started
writing some basic GUI applications. But don’t stop there: propose yourself to go one level up, programing something fun, like a Ping-Pong game maybe. For this, you can use a small TButton as the ball and a bigger button as the defender of the gate. Try to prevent the ball from hitting the left-hand margin of the form by moving the defender up and down with the arrows keys. Use a TTimer to update the position of the ball every 20 ms and use the OnKeyPress event of the form to check if the user pressed a key to move the defender.
Use Google/StackOverflow as you encounter problems.
Video tutorials
There are quite a few big Delphi websites out there. For German people, there is a high-quality forum about Delphi, in german language. Really freundlich people on that forum.
Lazy people should start with YouTube. Here are some interesting video, perfect for beginners:
Hello World tutorial - a quick introduction on how to create simple GUIs
A very very basic spell checker
A bit more advanced tutorial programming a tabbed interface.
Special videos
These tutorials show how quickly you can develop an advanced application, writing ALMOST NO code:
How to make an SQL application
Books
You can find many Delphi books on Amazon. As a beginner, you can also buy old Delphi books, as they are cheaper. I mentioned that the Delphi language is always backward compatible. This means that most things that you read in an old Delphi book it is still valid today. So, any Delphi 7 (and up) book is still good for a beginner.
However, I think that the most complete Delphi books come from Borland as a famous trio, AND THEY ARE FREE:
Delphi 7 Quick Start (a quick introduction into the IDE)
If you are a beginner, read the first chapters only – the ones that give you the introduction into the language. Later, as you advance, you can read the whole book.
Also, don’t forget to read the Appendix. Even if its name seems boring, I promise you will find interesting stuff there.
My books
If you like this book, stand by for my next books called:
The things they haven't told you
How to build and sell your own software
Summary
Delphi is a clean language. You will learn it as you use it.
There are many resources out there. Google is your friend.
What I have learned in the last 25 years of GUI development
Simple GUIs are better than complex GUIs
As a programmer, and as a computer geek I like my (and not only MY) programs to be configurable; to have lots and lots of options where I can click and configure the program at the lowest level. However, most computer users are not programmers, neither computer geeks!
Regular
computer users are simply confused when presented with too many
options. So, you might laugh if you want, but I am not joking: the
best program GUI (from the user’s point of view) might be this:
It took me some time to get over the instinct of “let’s make this program configurable”. Lesson learned.
Don't get me wrong. I am not saying that building a complex (I said complex; not complicated) GUI is a bad idea. It is bad ONLY for regular/average computer users… which are… majority.
My advice: Learn to trade between “too-many-options” and “no-options”. If your application addresses really non-savvy computer users (like older people) build a super simple GUI (don’t forget to use the big font, for elders and people with glasses) but let an open gate for more PC-savvy users: for example allow the user to switch between a basic GUI and a “full” GUI.
Let there be mockups!
Don’t
rely only on the “project requirements” document. The
customer/user doesn’t know what they want. As soon as you build
the application and deliver it to them, they will say “Hey! Why
is this button green? I wanted it in red!”.
Present a
mock-up as soon as possible. As soon as the customer sees the mock-up
it will realize that he wants 10 extra buttons that he forgot to
mention into the “project requirements”.
Present alternatives when the customer asks for unreasonable complicated GUI elements. They may actually like your idea better.
Save GUI state to file
Users love to resume work from where they stopped last time. Save your GUI status to an INI file. Restore the GUI to the exact same state when the program resumes. I do that in all my programs by simply calling two custom procedures that I built: SaveForm, LoadForm. These procedures will save the last paths used in dialog boxes ( SaveFile, LoadFile), the status of all checkboxes, radio buttons, spinboxes, etc.
I will publish some of the functionalities enumerated above in my next book.
What I have learned in the last 25 years of software development
You DON’T decide how your software must be! The customers decide this.
Get feedback
All you need in order to sell your product is a good product. If you build it, they will come. In order to build a good product, you need to know what your customers need. You obtain this information by staying in contact with your customers; by getting feedback.
This is so important that I put it in red and I still feel that I haven't stressed it enough.
Find beta-testers
Find users that are willing to test the software for you. There is always somebody in love with your software. They will do it even if you don’t ask it.
Some other people will show some interest in your software, but not quite enough to beta test it for free. Give them a free license in exchange for their feedback/opinion.
Good enough software
“Great software today is often preferable to perfect software tomorrow” - The Pragmatic developer, Hunt, 2010.
Release early (alpha/beta). Let the market know you are there. Don’t wait 2 years until you have the “golden” version. Continue to release as soon as you add new features.
Reuse code
Never in my life, I wrote a function twice (or used copy/paste to duplicate code). Reuse your code.
Organize your code
Every time I write a function that I might use it later, I test it and put it in my library. The library now consists of 178-pascal files grouped into packages like VCL, string-utils, hardware, graphics, internet, debugging, IO, etc.
Let there be templates
Instead of starting a new application from zero, I reuse a template. This template is a Delphi project that contains basic/generic code: an auto-updater, an about box, a EULA box, a main menu, code to respond to drag and drop, code to automatically save/restore the GUI to file, create a desktop shortcut on the first run, etc. The debugger/compiler options are also set to my preferred values.
If the template contains code that I don't need, I simply delete it - it is easier to delete code that to add it. Basically, it is a functional application that has no actual business/algorithm code inside. Only code for GUI.
This helps me develop a brand new program in minutes instead of hours.
Know how much your users don't know
I have seen so many computer gurus overestimating the knowledge of the average PC user. Remember that some users don’t really know the difference between Folder and File… or between hard-drive and memory or how to unpack a ZIP… or how to drag and drop… or…
Take them into consideration when you build your application.
Antivirus programs are your Nemesis
Antivirus programs are famous for their poor reliability. If in doubt, they will easily throw a false positive alarm. So, from time to time you will see your programs flagged as 'virus'. Ignore the small antivirus vendors and contact only the big ones to white list your program.
Use VirusTotal.com (a Google product) to check your application. VirusTotal is a collection of 62 antivirus engines. When you upload your application there, it will be scanned with all those 62 engines. Return two weeks later and scan it again. You may find that the number of engines that returned a false positive result, has increased. This happens as many small antivirus programs will “copy” the data (virus definitions) from big-brand antivirus products. Take measures if more than 3-4 big-brand antivirus programs report a false positive. If more than 50% of them report a false positive your website may get suspended for “spreading malware”.
When a bug was found, release a patch on the same day
Put a “check for updates” automatic function into your program. This will help you a lot if you release lots of updates.
Focus on the current version only
You want to be productive, don’t maintain multiple versions and don’t release updates for old versions. If the users of old versions complain give them cheap/free upgrade options to the current version.
Do you think you will lose money from possible upgrade licenses? Maybe, but maintaining multiple versions is so time-consuming that in the end, you will actually lose more money.
Avoid installers
Not every user has admin rights (especially in academia and corporations).
Therefore, don’t use installers that require admin/elevated privileges. What's the point to give to your user a program that cannot be installed (unless they have admin passwords).
Don't waste time writing a user manual
Don’t underestimate how impatient users are. Clearly, nobody reads the user manual. Less than 1% of the users that I have on my websites visited the user manual section. Users will rather email you than reading the manual. Instead of the user manual, use tool-tips. Abundantly. You will save a lot on “customer support”!
Feature requests
Do users keep asking for a feature? What are you waiting for? Implement it. This is how you improve your product.
Linux who?
Monies are on Windows and Mac. If you create commercial products, forget about Linux.
The Mac also has only a tiny portion of the market. However, Mac users have more money or are more willing to spend.
English is (generally) enough.
Find out who is your audience. Some of my programs are for academia, therefore, there is no point in having those programs in any other language than English. All people in academia speak (or at least, they SHOULD speak) English.
However, if you address exclusively to people in a country them you need to translate it.
Again: if you have a community that loves your product, they will translate the program for free. Just give them the tools. One popular option will be to put all language strings in an INI file. Anyone could open the file with a text editor and translate it.
One last advice
When your website has over 1000 visitors per day there will be someone that will love your program and someone that will hate your program. You will receive emails thanking you for the software you created and then you will also receive “your software is shit” emails (for the same software). You cannot satisfy anyone. Don't think too much at night about that. In the best-case scenario, try to figure out if you can extract some constructive information from the “bad” emails to improve your software.
Anyway, the rule of thumb is: if the users that send the complaint seem to be non-computer savvy then ignore the email. They have to learn how to properly use the computer first. However, if the user that complains is an advanced user, then it is time to worry. See, what you are doing wrong!
Summary
You DON’T decide how your software will be! The customers decide this.
Rely on your users, listen to their feedback, use them for beta testing.
Appendix
Files generated by Delphi
Ext |
Description |
Critical |
.DPR |
Delphi Project file: binds together all Pas files in your project (if you have more than one) |
Yes |
.DPROJ |
Holds information about the configuration/settings of the current project. Non-critical. If deleted, it will be automatically rebuilt and all project settings will be reset to factory default. |
Not really |
.PAS |
Delphi pascal file: holds the actual pascal file. |
Yes |
.DFM |
Delphi forms: holds the GUI elements |
Yes |
.DCU |
Delphi compiled unit. Holds the binary code resulted after compiling the Pas file. Equivalent to Obj files in C++. It can be deleted. |
No* |
.DSK |
Holds information about the state of the IDE for the current project (how windows were arranged on the screen, which windows were visible, etc). |
No |
.BPL |
Borland Pascal Library: some kind of DLL but with more “special powers”. Contains a package header and the concatenation of all .dcu files in the project. A single .dcp file is created for each package. |
No* |
.DCP |
Delphi Compiled Package. Related to BPL. A Delphi compiled package file is a binary image containing a package header and the concatenation of all Delphi compiled unit files in your package, including all symbol information required by the compiler and linker. |
No* |
.RES |
Binary file. Holds graphic resources (if any) for this project. Resource files are linked to a specific PAS file with the {$R *.RES} compiler directive. |
Yes |
.~* |
Backup file generated by IDE every time you press 'Save' |
No |
.MAP |
Binary file necessary for debugging. Automatically generated every time you compile. |
No |
.LOCAL |
User-specific project options. Just delete it. |
No! |
* Don't delete the DCU, DCP, BPL files of 3rd party libraries.
Fun facts (for geeks)
This Wikipedia page tells us the funny story of how PHP was NOT designed to become a programming language. PHP was some kind of HTML markup for a guy’s home page.
Pascal was designed to be keyboard friendly: you will type most of the code without really having to stretch your fingers to reach for the Ctrl or Shift keys.
Programs you might (should) know built with Delphi
WinRAR, Skype, Nero Burning Rom, Macromedia DreamWeaver, InstallAware, InnoSetup, Partition Magic, Panda Antivirus, Aida64, Total Commander, Ultra ISO, MySQL Admin tools, Gold Wave.
Styles
This code lets the user choose at run-time the style he wants to use. PopulateStyles will read the available styles that you incorporated into the program (via Project Option) and put them in a list box. The ListBoxClick event handler will respond to the user's mouse click by applying the current style.
procedure TfrmSkins.PopulateStyles; // Call this at program startup
VAR
StyleName: string;
begin
ListBox.Clear;
//Enumerates all the styles linked in the executable
for StyleName in TstyleManager.StyleNames do
ListBox.Items.Add(StyleName); // Put the current style name into ListBox
end;
procedure TfrmSkins.ListBoxClick(Sender: TObject);
var CurStyle: string;
begin
// Exit if no styles present/loaded?
if ListBox.ItemIndex < 0 then EXIT; // ItemIndex is the current selected item
CurStyle:= ListBox.Items[ListBox.ItemIndex];
if CurStyle > '' then
begin
TStyleManager.SetStyle(CurStyle);
Application.ProcessMessages;
BringToFront;
end;
end;
Example of ASM code in Pas file:
Delphi can mix assembler and pascal code in the same file, even in the same function.
// Mix two colors. Author: Yurii Zhukow www.infoteh.ru
function MixBytes(FG, BG, BlendPower: Byte): Byte;
asm
push bx // push some regs
push cx
push dx
mov DH,BlendPower // remember Transparency value
mov BL,FG // filling registers with our values
mov AL,DH // BL = ForeGround (FG)
mov CL,BG // CL = BackGround (BG)
xor AH,AH // Clear High-order parts of regs
xor BH,BH
xor CH,CH
mul BL // AL=AL*BL
mov BX,AX // BX=AX
xor AH,AH
mov AL,DH
xor AL,$FF // AX=(255-TRANS)
mul CL // AL=AL*CL
add AX,BX // AX=AX+BX
shr AX,8 // Finally we have mixed value in AL
pop dx // Clean up
pop cx
pop bx // Bye, dear Assembler - we go home to Delphi!
end;
Delphi release dates:
Rel name |
Ver. |
Product name |
Release date |
1 |
1.0 |
Borland Delphi |
1995-02-14 |
2 |
2.0 |
Borland Delphi 2 |
1996-02-10 |
3 |
3.0 |
Borland Delphi 3 |
1997-08-05 |
4 |
4.0 |
Borland Delphi 4 |
1998-07-17 |
5 |
5.0 |
Borland Delphi 5 |
1999-08-10 |
6 |
6.0 |
Borland Delphi 6 |
2001-05-21 |
7 |
7.0 |
Borland Delphi 7 |
2002-08-09 |
8 |
8.0 |
Borland Delphi 8 for .NET |
2003-12-22 |
2005 |
9.0 |
Borland Delphi 2005 |
2004-10-12 |
2006 |
10 |
Borland Delphi 2006 |
2005-11-23 |
2007 |
11 |
CodeGear Delphi 2007 |
2007-03-16 |
2009 |
12 |
CodeGear Delphi 2009 |
2008-08-25 |
2010 |
14 |
Embarcadero RAD Studio 2010 |
2009-08-15 |
XE |
15 |
Embarcadero RAD Studio XE |
2010-08-30 |
XE2 |
16 |
Embarcadero RAD Studio XE2 |
2011-09-02 |
XE3 |
17 |
Embarcadero RAD Studio XE3 |
2012-09-03 |
XE4 |
18 |
Embarcadero RAD Studio XE4 |
2013-04-22 |
XE5 |
19 |
Embarcadero RAD Studio XE5 |
2013-09-11 |
XE6 |
20 |
Embarcadero RAD Studio XE6 |
2014-04-15 |
XE7 |
21 |
Embarcadero RAD Studio XE7 |
2014-09-02 |
XE8 |
22 |
Embarcadero RAD Studio XE8 |
2015-04-07 |
10 Seattle |
23 |
Embarcadero RAD Studio 10 Seattle |
2015-08-31 |
10.1 Berlin |
24 |
Embarcadero RAD Studio 10.1 Berlin |
2016-04-20 |
10.2 Tokyo |
25 |
Embarcadero RAD Studio 10.2 Tokyo |
2017-03-22 |
10.3 Rio |
26 |
Embarcadero RAD Studio 10.3 Rio |
2019-07-18 |
Notes and credits
Disclaimer
No “Hungarians” were harmed during the production of this book above.
Contact
Have you found a mistake? Do you disagree with some of my statements? Or maybe you simply want to exchange impressions?
You can contact me at the following gmail address: mrGMoraru
I will gladly answer you.
Credits
Cover photo by Erol Ahmed.
GranpaGuru photo by mentatdgt (Pexels.com)